Python's assert statement

What is assert?

Python's assert statement can be said to be a good debugging tool, mainly used to test whether a condition is met. If the test conditions are met, nothing is done, which is equivalent to executing the pass statement; if the test conditions are not met, an exception AssertionError will be thrown and specific error information (optional) will be returned.

Its specific syntax is as follows:


assert_stmt ::=  "assert" expression ["," expression]

Let's first look at a simple form of assert expression, such as the following example:


assert 1 == 2

It is equivalent to the following two lines of code:


if __debug__:
    if not expression: raise AssertionError

Let's look at the form of assert expression1, expression2, such as the following example:


assert 1 == 2,  'assertion is wrong'

It is equivalent to the following two lines of code:


if __debug__:
    if not expression1: raise AssertionError(expression2)

Here __debug__ is a constant. If the Python program is executed with the -O option, such as Python test.py -O, then all assert statements in the program will be invalid, and the constant __debug__ will be False; otherwise, __debug__ will be True.

However, it should be noted that it is illegal to assign a value to the constant __debug__ directly, because its value is determined when the interpreter starts running and cannot be changed in the middle.

In addition, remember not to add parentheses when using assert, as in the following example:


assert(1 == 2, 'This should fail')
# 输出
<ipython-input-8-2c057bd7fe24>:1: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert(1 == 2, 'This should fail')

If you write in this way, regardless of whether the expression is right or wrong (for example, 1 == 2 here is obviously wrong), the assert check will never fail, and the program will only give you SyntaxWarning.

The correct way of writing should be the following way of writing without parentheses:


assert 1 == 2, 'This should fail'
# 输出
AssertionError: This should fail

In general, the role of assert in the program is to do some internal self-checking of the code. Use assert, you are pretty sure. This condition will happen or it will never happen.

For example, if you have a function, one of the parameters is the gender of the person, because the gender is only male and female (here only refers to the biological gender), you can use assert to prevent illegal input of the program. If your program has no bugs, then assert will never throw an exception; and once it throws an exception, you know that there is a problem with the program, and you can easily locate the source of the error based on the error message.

Usage of assert

After talking about the basic syntax and concepts of assert, let's take a look at the usage of assert in Python through some practical application examples, and figure out the usage scenarios of assert.

The first example, suppose you are currently using geek time to do column promotion activities and prepare to discount some columns, so you need to write an apply_discount() function in the background, requiring the input to be the original price and discount, and the output is discounted price. Then, we can roughly write as follows:


def apply_discount(price, discount):
    updated_price = price * (1 - discount)
    assert 0 <= updated_price <= price, 'price should be greater or equal to 0 and less or equal to original price'
    return updated_price

As you can see, after calculating the new price, we also wrote an assert statement to check the discounted price. This value must be greater than or equal to 0 and less than or equal to the original price, otherwise an exception will be thrown.

We can try to enter a few sets of numbers to verify this function:


apply_discount(100, 0.2)
80.0

apply_discount(100, 2)
AssertionError: price should be greater or equal to 0 and less or equal to original price

Obviously, when the discount is 0.2, the output is 80, which is no problem. But when discount is 2, the program throws the following exception:


AssertionError:price should be greater or equal to 0 and less or equal to original price

In this way, if the developer modifies the relevant code or adds a new function, which causes the discount value to be abnormal, we can easily find the problem when we run the test. As I said at the beginning, the addition of assert can effectively prevent the occurrence of bugs and improve the robustness of the program.

Let's look at another example, the most common division operation, which is often encountered in any field of calculation. Also take Geek Time as an example. If the Geek Time background wants to know the average sales price of each column, then the total sales and the number of sales need to be given, so that the average sales price can be easily calculated:


def calculate_average_price(total_sales, num_sales):
    assert num_sales > 0, 'number of sales should be greater than 0'
    return total_sales / num_sales

Similarly, we have also added an assert statement to stipulate that the number of sales must be greater than 0, so as to prevent the background from calculating the prices of columns that have not yet been sold.

In addition to these two examples, in actual work, assert has some very common uses, such as the following scenario:


def func(input):
    assert isinstance(input, list), 'input must be type of list'
    # 下面的操作都是基于前提:input必须是list
    if len(input) == 1:
        ...
    elif len(input) == 2:
        ...
    else:
        ... 

Here, all operations in the function func() are based on the premise that the input must be a list. Is it a familiar requirement? Then it is necessary for us to add an assert check at the beginning to prevent program errors.

Of course, we have to make specific analysis based on specific circumstances. For example, in the above example, we can add assert because we are pretty sure that the input must be a list, not other data types.

If your program allows input to be other data types and has different processing methods for different data types, then you should write an if else conditional statement:


def func(input):
    if isinstance(input, list):
        ...
    else:
        ...

assert error example

Earlier we talked about so many use scenarios of assert, which may give you an illusion, or may make you a little confused: assert can be used in many places, so, can many if conditional statements be replaced with assert? It is not accurate to think so. Next, let's take a look at a few typical wrong usages and avoid some usages that are taken for granted.

Taking Geek Time as an example, we assume the following scenario: Sometimes it is necessary to delete some columns that have been online for a long time in the background, so the relevant developers have designed the following column deletion function.


def delete_course(user, course_id):
    assert user_is_admin(user), 'user must be admin'
    assert course_exist(course_id), 'course id must exist'
    delete(course_id)

Geek Time stipulates that you must be an admin to delete a column, and the column course must exist. Some students found that they were very familiar with the requirements, so they added the corresponding assert check in front. So I want you to think about it, is it right to write this way?

The answer is obviously no. You may think that this is correct from the point of view of code function. But in actual engineering, basically no one would write that way. why?

It should be noted that, as I said earlier, the assert check can be turned off. For example, when running a Python program, adding the -O option will make assert invalid. Therefore, once the assert check is turned off, the two functions user_is_admin() and course_exist() will not be executed. This will lead to:

  • Any user has the right to delete column courses;
  • And, regardless of whether the course exists or not, they can delete it forcibly.

This will obviously bring huge security holes to the program. Therefore, the correct approach is to use conditional statements to perform corresponding checks and throw exceptions reasonably:


def delete_course(user, course_id):
    if not user_is_admin(user):
        raise Exception('user must be admin')
    if not course_exist(course_id):
        raise Exception('coursde id must exist')
    delete(course_id)  

Let's look at another example. If you want to open a file and perform a series of operations such as data reading and processing, then the following writing is obviously incorrect:


def read_and_process(path):
    assert file_exist(path), 'file must exist'
    with open(path) as f:
      ...

Because of the use of assert, it shows that you forcibly specify that the file must exist, but in fact, in many cases, this assumption does not hold. In addition, open file operations may also trigger other exceptions. Therefore, the correct approach is to handle exceptions, using try and except to solve:


def read_and_process(path):
    try:
        with open(path) as f:
            ...
    except Exception as e:
            ...  

In general, assert does not apply to run-time error checks.

Guess you like

Origin blog.csdn.net/qq_41485273/article/details/114240550