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

Python 在涉及真值判断(Truth Value Testing)时,语法很简便。

比如,在判断某个对象是否不为 None 时,或者判断容器对象是否不为空时,并不需要显示地写出判断条件,只需要在 if 或 while 关键字后面直接写上该对象即可。

下图以列表为例,if my_list 这个简短的写法可以表达出两层意思:

如果需要作出相反的判断,即"如果为 None 或为空",只需要写成if not my_list 即可。

与众不同的真值判断方式通常而言,当一个值本身是布尔类型时,写成"if xxx"(如果真),在语义上就很好理解。如果 xxx 本身不是布尔类型时,写成"if xxx"(如果某东西),则在语义上并不好理解。

在 C/C++/Java 之类的静态语言中,通常要先基于 xxx 作一个比较操作,比如"if (xxx == null)",以此得到一个布尔类型的值的结果,然后再进行真值判断。否则的话,若"if xxx"中有非布尔类型的值,则会报类型错误。

Python 这门动态语言在这种场景中表现出了一种灵活性,那么,我们的问题来了:为什么 Python 不需要先做一次比较操作,直接就能对任意对象作真值判断呢?

先来看看文档 中对真值判断的描述:

简单而言,Python 的任何对象都可以用在 if 或 while 或布尔操作(and、or、not)中,默认情况下认为它是 true,除非它有__bool__() 方法返回False 或者有__len__() 方法返回0 。

对于前面的例子,my_list 没有__bool__() 方法,但是它有__len__() 方法,所以它是否为 true,取决于这个方法的返回值。

真值判断的字节码接着,我们继续刨根问底:Python 为什么可以支持如此宽泛的真值判断呢?在执行if xxx 这样的语句时,它到底在做些什么?

对于第一个问题,Python 有个内置的 bool() 类型,可以将任意对象转化成布尔值。那么,这是否意味着 Python 在进行真值判断时,会隐式地 调用 bool() 呢(即转化成if bool(xxx))?(答案为否,下文有分析)

对于第二个问题,可以先用dis 模块来查看下:

POP_JUMP_IF_FALSE指令对应的是 if 语句那行,它的含义是:

If TOS is false, sets the bytecode counter to target. TOS is popped.

如果栈顶元素为 false,则跳转到目标位置。

这里只有跳转动作的描述,仍看不到一个普通对象是如何变成布尔对象的。

Python 在解释器中到底是如何实现真值判断的呢?

可以看出,对于布尔类型的对象(即 Py_True 和 Py_False),代码会进入到快速处理的分支;而对于其它对象,则会用 PyObject_IsTrue() 计算出一个 int 类型的值。

PyObject_IsTrue() 函数在计算过程中,依次会获取 nb_bool、mp_length 和 sq_length 的值,对应的应该就是 __bool__() 和 __len__() 这两个魔术方法的返回值。

这个过程就是前文中所引用的官方文档的描述,正是我们想要找的答案!

另外,对于内置的 bool(),它的核心实现逻辑正是上面的 PyObject_IsTrue() 函数,源码如下(boolobject.c):

所以,Python 在对普通对象作真值判断时,并没有隐式地调用 bool(),相反它调用了一个独立的函数(PyObject_IsTrue()),而这个函数又被 bool() 所使用。

也就是说,bool() 与 if/while 语句对普通对象的真值判断,事实上是基本相同的处理逻辑。 知道了原理,就会明白if bool(xxx) 这种写法是多此一举的了(我曾见到过)。

至此,我们已经回答了前文中提出的问题。

验证真值判断的过程接下来,有 3 个测试例子,可以作进一步的验证:

你可以暂停而思考下:bool(Test1) 与 bool(Test1()) 各是什么结果?然后依次判断剩下的两个类,结果又会是什么?

揭晓答案:

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

bool(Test1()) # Truebool(Test2()) # Falsebool(Test3()) # True原因如下:

类对象没被实例化时,bool() 不会调用它的 __bool__() 或 __len__() 这两个魔术方法类对象被实例化后,若同时存在 __bool__() 或 __len__() 魔术方法,则 bool() 会先调用 __bool__() 方法(PS:这个方法要求返回值必须为 bool 类型,因此只要有它,就必然不需要再用__len__() 方法来判断真假)数字类型如何作真值判断?除了这 3 个例子,还有一种情况值得验证,那就是对于数字类型,它们是怎么做真值判断的呢?

我们可以验证一下数字类型是否拥有那两个魔术方法:

hasattr(2020, "__bool__")hasattr(2020, "__len__")不难验证出,数字拥有的是 __bool__() 魔术方法,并没有__len__() 魔术方法,而且所有类型的数字其实被分成了两类:

__bool__() 返回 False:所有表示 0 的数字,例如0, 0.0, 0j, Decimal(0), Fraction(0, 1)__bool__() 返回 True:所有其它非 0 的数字文章小结Python 中if xxx 这种简便的写法,虽然是正规的真值判断语法,并它但并不符合常规的语义。在 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 的数字为 False,其它数字都为 True新上课程

课程:《MySQL数据库入门实战- Python版》

课程介绍:MySQL是当前互联网非常重要的数据存储技术,大量的用户信息、订单信息、客户信息都存储在MySQL,掌握MySQL甚至成为衡量一名程序员的重要技术,而非技术的运营、产品也经常需要使用SQL去查询数据进行分析。本课程讲解MYSQL的使用技术,结合Python语言的应用,也实现了自动化的操作和处理。讲师:裴帅帅讲师介绍:前百度资深大数据工程师,现某一线互联网推荐系统架构师,8年大数据、机器学习研发经验,常年使用和实战Python/Java双语言。课程优惠:原价198元,目前上线限时5 折优惠,普通用户只需要99即可获得该门课程永久权限

「点点赞赏,手留余香」

赞赏

  • 0人赞过
0
0
0
评论 0 请文明上网,理性发言

相关文章

  • 现状揭秘:Python岗位大厂50K起?程序员:心态崩了! 屠杀各种榜单,拿下语言排行榜的Python,薪酬真的如同网传开挂了吗?笔者在脉脉上发现了这样的一个信息: 但Python真的这么火?笔者准备去全网帮你们"打探"一下,让大家了解真实的Python市场。 (数据来自看准网2019年11.4日数据)从上图看,P
    饯莸抟 9 2 0 条评论
  • 一些科学家开始使用比Python性能更强大的新编程语言了,它和Python一样能够"立等可取",而且计算能力还更强。 原文作者:JeffreyM.Perkel2015年,生物信息学家JohannesK?ster还是(用他自己的话说)"差不多全职写Python的"。他当时已经用Python写过一个备受欢迎的工具--流程管
    乱世知己琪哪 8 1 0 条评论
  • 为什么Python不是未来的编程语言? 作者|RheaMoutafis 译者|弯月,责编|郭芮 出品|CSDN(ID:CSDNnews) 以下是译文:Python经过了几十年的努力才得到了编程社区的赏识。自2010年以来,Python得到了蓬勃发展,并最终超越了C、C#、Java和JavaScript。 但是,这种趋
    天王地虎happy 4 0 0 条评论
  • Python之父,现在成了微软的打工人。 没错,全世界程序员们最喜欢的编程语言的创造者,自述耐不住退休生活的寂寞,重返岗位发光发热。 GuidoVanRossum,打算去微软做些什么?又为什么选择微软? Python之父加入微软干什么? 几个小时前,GuidoVanRossum本人发推宣布了这个消息,他说:"退休生
    路茫之间 5 2 0 条评论
  • 大数据文摘出品 来源:medium 编译:陈之炎、coolboy 自从20世纪90年代初发布以来,Python一直相当火爆,在这二十多年里,它的流行程度远远超过了C、C#、Java甚至Javascript。 虽然Python在数据科学和机器学习领域占主导地位,甚至是科学和数学计算领域的主角,但与Julia、Swift
    ahdhtang 5 0 0 条评论
  • Bottle是一个超轻量级的python库。说是库,其本身只由一个4000行左右的文件构成,并且不需要任何依赖,只靠python标准库即可运作。 和它本身的轻便一样,Bottle库的使用也十分简单。相信在看到本文前,读者对python也已经有了简单的了解。那么究竟何种神秘的操作,才能用百行代码完成一个服务器的功能?让我
    超级超级 4 3 0 条评论
  • 终于,Python3.11正式版发布了! 2020年1月1日,Python官方结束了对Python2的维护,这意味着Python2已完全退休,进入了Python3时代。打从进入3版本以来,Python官方已经发布了众多修改分支,现在来到了最新的版本Python3.11。 其实研究界有个不公开的秘密,那就是Python
    書与南柯RD 6 0 0 条评论
  • 据澎湃新闻近日消息,山东省在其最新出版的小学信息技术六年级教材中,加入了Python的内容。在此之前,编程界也一直有传言,称浙江省将对中学信息技术教材进行改动,弃VB(VisualBasic6.0)而选用Python。 事情真的会如程序员们期待的那样发展吗?Python语言又为什么突然受到了格外的重视呢? VB做错了什
    高数次 4 1 0 条评论
  • 模拟键盘操作执行自动化任务,我们常用的有pyautowin等自动化操作模块。但是这些模块有一个很大的缺点,编译的时候非常依赖windows的C语言底层模块。 今天介绍的这个模块叫做keyboard它有一个最大的优点:纯Python原生开发,编译时完全不需要依赖C语言模块。一行命令就能完成安装,非常方便。 1.准备 首
    爱在你身边白羊 6 8 0 条评论
  • 来源:内容由半导体行业观察(ID:icbank)编译自hpcwire,谢谢。 近日,加州大学洛杉矶分校引入了一种新的硬件描述语言PyGears,以实现基于可重用组件和高级Python构造的敏捷芯片设计理念。PyGears是对快速发展的软件世界的回应,这要求硬件设计与可扩展和智能未来的需求保持同步。 据官网介绍PyGea
    文仔230 7 0 0 条评论