当前位置: 首页 > 后端技术 > Python

为什么Python支持任意真值判断?

时间:2023-03-26 19:14:19 Python

作者:猫下豌豆花来源:Python猫Python在真值检验方面的语法非常简单。比如判断一个对象是否不为None,或者判断一个容器对象是否不为空,不需要显式写判断条件,直接在if或者while关键字后面写对象即可。下图以列表为例。ifmy_list的简写可以表达两种意思:如果需要做相反的判断,即“ifitisNoneorempty”,只需要写成ifnotmy_list即可。判断真值的不同方式一般来说,当一个值本身是布尔类型时,写成“ifxxx”(如果为真)在语义上很容易理解。如果xxx本身不是布尔类型,写成“ifxxx”(ifsomething)在语义上是不可理解的。在C/C++/Java等静态语言中,通常需要根据xxx进行比较运算,比如“if(xxx==null)”,得到一个布尔值的结果,然后执行一个真正的价值判断者。否则,如果“ifxxx”中有非布尔值,就会报类型错误。Python这种动态语言在这种情况下显示出一种灵活性。那么,我们的问题来了:为什么Python不需要先做比较运算,就可以直接对任意对象进行真值判断呢?先看文档中关于真值判断的描述:简单来说,Python中的任何对象都可以用if或while或布尔运算(and,or,not),默认认为是true除非它有一个返回False的__bool__()方法或者一个返回0的__len__()方法。对于前面的例子,my_list没有__bool__()方法,但是它有一个__len__()方法,那么它是否是否为真取决于此方法的返回值。——真值判断字节码接下来,我们继续深究:为什么Python可以支持如此广泛的真值判断?它执行ifxxx这样的语句时到底在做什么?对于第一个问题,Python有一个内置的bool()类型,可以将任何对象转换为布尔值。那么,这是否意味着Python在进行真值判断(即转换为ifbool(xxx))时会隐式调用bool()呢?(答案是否定的,下面有分析)对于第二个问题,可以先用dis模块查看:POP_JUMP_IF_FALSE指令对应if语句的那一行,其含义是:如果TOS为假,则置_target_的字节码计数器。TOS弹出。如果栈顶元素为false,则跳转到目标位置。这里只有跳转动作的描述,我还是看不出一个普通的对象是如何变成布尔对象的。Python在解释器中是如何实现真值判断的?——真值判断的源码实现在微信群成员Jo的帮助下,找到了CPython的源码(文件:ceval.,代码会进入快速处理分支;对于其他对象,一个int类型的值会用PyObject_IsTrue()进行计算,在计算过程中,PyObject_IsTrue()函数会依次获取nb_bool、mp_length和sq_length的值,这两个魔术方法的返回值应该对应__bool__()和__len__()。这个过程就是上一篇引用的官方文档的描述,就是我们要找的答案!另外,对于内置的bool(),其核心实现逻辑就是上面的PyObject_IsTrue()函数,源码如下(boolobject.c):因此,Python并没有隐式调用bool(),而是调用了一个单独的函数(PyObject_IsTrue()),该函数又被bool()使用。即可以说,bool()和if/while语句基本上判断普通对象真假的处理逻辑是一样的。知道了原理,你就会明白,如果bool(xxx)写的多此一举(我以前见过)。至此,我们已经回答了上一篇文章中提出的问题。——验证真假判断的过程接下来有3个测试例子可以进一步验证:大家可以暂停思考:bool(Test1)和bool(Test1())的结果是什么?然后依次判断剩下的两个类,结果会是什么?揭晓答案:bool(Test1)#Truebool(Test2)#Truebool(Test3)#Truebool(Test1())#Truebool(Test2())#Falsebool(Test3())#True原因如下:当上课对象没有实例化,bool()不会调用它的__bool__()或__len__()魔法方法类对象实例化后,如果同时有__bool__()或__len__()魔法方法,bool()会先调用调用__bool__()方法(PS:该方法要求返回值必须是bool类型,所以只要有,就不用再用__len__()方法判断真假了)———如何判断数字类型的真实价值?除了这三个例子,还有一种情况值得验证,那就是对于数字类型,他们是如何判断真值的?我们可以验证数字类型是否有那两个魔术方法:hasattr(2020,"__bool__")hasattr(2020,"__len__")不难验证数字有__bool__()魔术方法,没有__len__()魔术方法,所有类型的数字其实分为两类:__bool__()返回False:所有代表0的数字,比如0,0.0,0j,Decimal(0),Fraction(0,1)__bool__()ReturnTrue:其他所有不为0的数——文章摘要Python中ifxxx的简单写法,虽然是形式化的真值判断语法,但不符合约定俗成的语义。在C/C++/Java这样的语言中,要么xxx本身就是一个布尔值,要么是一个返回布尔值的运算,但是在Python中,这个“xxx”可以是任意的Python对象!本文通过对CPython解释器的文档、字节码和源码的一步步分析,发现Python判断真值的过程并不简单,可以提炼出以下关键点:if/while是隐式布尔运算符:它们除了具有“判断”真假的功能外,还具有从普通对象隐式计算布尔结果的功能。实际操作是由解释器根据“POP_JUMP_IF_FALSE”指令完成的。它的核心逻辑与内置的bool()共享一个底层方法。真值判断过程依赖于两个神奇的方法:除非被判断对象有一个__bool__()方法返回False或者有一个__len__()方法返回0,否则布尔运算的结果为True。两种魔术方法总是先计算__bool__()数字类型,也可以做真值判断:数字有__bool__()魔术方法,但没有__len__()魔术方法,除了代表0的数字,其他数字两者都是真实的。