Why does Python support arbitrary truth-value judgments?

> This article is from the "Why Python" series, please see all articles

Python has a simple syntax when it comes to 真值判断( Truth Value Testing ).

For example, when judging whether an object is not None, or when judging whether a container object is not empty, it is not necessary to write the judgment condition explicitly, just write the object directly after the if or while keyword.

The following figure takes the list as an example. if my_listThis short writing can express two meanings:

If you need to make the opposite judgment, that is, "if None or empty", just write it as if not my_list.

A different way of judging truth value

In general, when a value is itself a boolean, it is semantically well understood to write "if xxx" (if true). Writing "if xxx" (if something) when xxx itself is not a boolean is not semantically easy to understand.

In C/C++/Java and the like 静态语言, it is usually necessary to perform a comparison operation based on xxx, such as "if (xxx == null)", to obtain a result of a Boolean value, and then perform a true value judgment. Otherwise, if there is a non-Boolean value in "if xxx", a type error will be reported.

Python 动态语言shows a kind of flexibility in this scenario, then, our question is: Why does Python not need to do a comparison operation first, and can directly judge the truth value of any object?

Let's first take a look at the description of truth judgment in the documentation :

Simply put, any object in Python can be used in an if or while or a boolean operation (and, or, not), and it is considered true by default unless it has a __bool__() method return Falseor has a __len__() method returns 0.

For the previous example, my_list does not have a __bool__() method, but it does have a __len__() method, so whether it is true or not depends on the return value of this method.

Bytecode for truth value judgment

Then, let's go to the bottom of the question: Why does Python support such a broad truth value judgment? if xxxWhat exactly is it doing when such a statement is executed ?

For the first question, Python has a built-in bool() type that can convert any object into a boolean value. So, does this mean that Python will 隐式地call bool() (ie, convert to if bool(xxx)) when making a truth value judgment? (The answer is no, there is an analysis below)

For the second question, you can use the dismodule to view it first:

POP_JUMP_IF_FALSEThe instruction corresponds to the line of the if statement, which means:

> If TOS is false, sets the bytecode counter to target . TOS is popped. > > If the top element of the stack is false, jump to the target position.

There is only a description of the jump action here, and I still can't see how an ordinary object becomes a Boolean object.

How does Python implement truth value judgment in the interpreter?

The source code implementation of truth value judgment

With the help of WeChat group friend Jo, I found the source code of CPython (files: ceval.c, object.c):

It can be seen that for objects of boolean type (ie Py_True and Py_False), the code will enter the fast processing branch; for other objects, it will use PyObject_IsTrue() to calculate a value of type int.

During the calculation process, the PyObject_IsTrue() function will obtain the values ​​of nb_bool, mp_length and sq_length in turn, corresponding to the return values ​​of the two magic methods __bool__() and __len__().

This process is the description of the official document cited in the previous article, and it is the answer we are looking for!

In addition, for the built-in bool(), its core implementation logic is the PyObject_IsTrue() function above. The source code is as follows (boolobject.c):

Therefore, Python does not implicitly call bool() when making truth judgments for ordinary objects. Instead, it calls a separate function (PyObject_IsTrue()), which is used by bool().

That is to say, bool() and if/while statements are in fact basically the same processing logic for the truth value judgment of ordinary objects. Knowing the principle, you will understand that if bool(xxx)this way of writing is superfluous (I have seen it).

So far, we have answered the questions posed in the previous article.

The process of verifying truth-value judgments

Next, there are 3 test examples that can be further verified:

You can pause and think: What is the result bool(Test1)of bool(Test1())each ? Then judge the remaining two classes in turn, what will be the result?

Reveal the answer:

bool(Test1)    # True
bool(Test2)    # True
bool(Test3)    # True

bool(Test1())  # True
bool(Test2())  # False
bool(Test3())  # True

The reasons are as follows:

  • When the class object is not instantiated, bool() will not call its two magic methods __bool__() or __len__()
  • After the class object is instantiated, if there is a __bool__() or __len__() magic method at the same time, the bool() will call the __bool__() method first (PS: This method requires that the return value must be of bool type, so as long as there is a It will definitely not need to use the __len__() method to judge true and false)

How to judge the truth value of a numeric type?

In addition to these three examples, there is another situation worthy of verification, that is , how do they make truth value judgments for numeric types?

We can verify that the number type has those two magic methods:

hasattr(2020, "__bool__")
hasattr(2020, "__len__")

It is not difficult to verify that numbers have the __bool__() magic method, but not the __len__() magic method, and all types of numbers are actually divided into two categories:

  • __bool__()Return False: all numbers that represent 0, e.g. 0, 0.0, 0j, Decimal(0),Fraction(0, 1)
  • __bool__()Returns True: all other non-zero numbers

Article summary

This simple way of writing in Python if xxx, although it is a regular truth value judgment syntax, and it does not conform to conventional semantics. In languages ​​such as C/C++/Java, either xxx itself is a boolean value or an operation that returns a boolean value, but in Python, this "xxx" can be any Python object. !

Through the step-by-step analysis of the source code of the document, bytecode and CPython interpreter, this paper finds that the truth judgment process of Python is not simple, and the following points can be extracted:

  • if/while are implicit Boolean operators: in addition to their function of "judging" true and false, they also have the function of implicitly computing ordinary objects to Boolean results. The actual operation is done by the interpreter according to the "POP_JUMP_IF_FALSE" instruction, and its core logic shares a low-level method with the built-in bool()
  • The truth value judgment process relies on two magic methods: unless the judged object has a __bool__() method returned Falseor a __len__() method returned 0, the result of the boolean operation is True. Both magic methods always evaluate __bool__() first
  • The number type can also be used for truth value judgment: the number has the __bool__() magic method, but there is no __len__() magic method, except that the number representing 0 is False, other numbers are True

If you think this article is a good analysis, then you should like these articles:

1. Why does Python use indentation to divide code blocks?

2. Is Python's indentation an anti-human design?

3. Why does Python not use a semicolon as a statement terminator?

4. Why does Python not have a main function? Why do I not recommend writing the main function?

5. Why does Python recommend snake-like nomenclature?

6. Why does Python not support the i++ auto-increment syntax and the ++ operator?

7. Why does Python only need one statement "a,b=b,a" to directly exchange two variables?

8. Why does Python use the # sign as a comment?

9. Why does Python have a pass statement?

10. Why does Python have a strange "..." object?

This article belongs to the "Why Python" series (produced by Python Cat), which mainly focuses on topics such as Python's syntax, design, and development. It tries to show the charming charm of Python by starting with "why" questions. All articles will be archived on Github, project address: https://github.com/chinesehuazhou/python-whydo

{{o.name}}
{{m.name}}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324129092&siteId=291194637