输出全部真因子python python输出真值表_Python


上回书说到

在前不久写下的文章《[Python]判断序列是否为空的正确姿势》中,我们曾讨论如下用于判断一个列表是否为空的代码:


# 判断a是否为空列表
 a = []
 if not a:
     print('This list is empty!')
 
 # 等价于
 a = []
 if len(a) == 0:
     print('This list is empty!')


现在我们深入思考一下,在这段代码中,Python当然是不能像肚子里的蛔虫一样,知道我们用if语句的目的是判断a的长度是否为0,那么它究竟是通过什么样的机制来对一个list类型的变量进行True/False的真值推断呢?进一步地,对于其他所有对象的真值推断,Python是否有一个统一的机制呢?

答案是:Python确实有一个简单而有效的机制对所有对象进行隐式布尔值(implicit booleanness)的推断,下面将进行进一步介绍。

文档是程序猿最好的朋友

Python的官方文档对真值测试(Truth Value Testing)进行了严格的描述,摘录如下:

https:// docs.python.org/3/libra ry/stdtypes.html#truth
Any object can be tested for truth value, for use in an if or while condition or as operand of the Boolean operations below.
By default, an object is considered true unless its class defines either a __bool__() method that returns False or a __len__() method that returns zero, when called with the object. Here are most of the built-in objects considered false:

  • constants defined to be false: None and False.
  • zero of any numeric type: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
  • empty sequences and collections: '', (), [], {}, set(), range(0)

Operations and built-in functions that have a Boolean result always return 0 or False for false and 1 or True for true, unless otherwise stated. (Important exception: the Boolean operations or and and always return one of their operands.)

任何对象都可以在ifwhile语句或andor等布尔操作符中进行真值测试(Truth Value Testing),测试的结果默认是True,除非一下两种情况的任何一种发生:

  • 该对象的类定义了__bool__函数,且该函数返回False
  • 该对象的类定义了__len__函数,且该函数返回0

另外,以下内置对象被认为是False:

  • 常量NoneFalse
  • 值为0的数值类型:0, 0.0, 0j, Decimal(0), Feaction(0, 1)
  • 空的序列和容器类型:'', (), [], {}, set(), range(0)

文档的优势在于简洁严谨,而本文的最终目的,是把问题讲清楚,讲明白。

__bool____len__

__bool____len__是Python中的两个魔术方法(magic method)。所谓魔术方法,就是Python中一般不会被直接显示调用,而是通过类的其他行为隐式调用的一类特殊方法。也就是说,这类方法在Python中有特殊的用途,即使你没有主动调用,他们也会不知不觉地被其他行为触发调用,比如今天将要介绍的__bool____len__,你可能之前还不知道,当你用ifwhile对某个对象进行真值判定时,这两个函数竟然可能会被调用!

所以,如果你在类中定义了这些方法的同名方法,请确定自己知道自己在做什么,否则程序可能会产生奇怪的行为~你可能会问,那我每次在自己定义方法时还需要去查表以保证我的命名没有冲突吗?不用担心,魔术方法有个显著的共同特点,就是名称总是由一对双下划线包围,这样做正是为了避免命名冲突。你只需保证自己命名的函数不以双下划线开头并结尾即可。

关于魔术方法的更多秘密,我们留待以后再专门讨论,这里只介绍今天将要用到的两个。

__bool__函数是用于表征类的真值属性的方法,如果要在类中定义__bool__函数,你应该保证:

  • 该方法返回一个布尔值TrueFalse,否则被调用时很可能抛出TypeError异常。
  • 该方法逻辑上符合你对该类的真值的设定,否则你无法利用它得到一个有意义的真值表征。

注意__bool__函数是Python3的魔术方法,而在Python2中,对应的是__nonzero__

__len__函数是用于表征类的长度属性的方法,如果要在类中定义__len__函数,你应该保证:

  • 该方法返回一个int类型,否则被调用时很可能抛出TypeError异常。
  • 该方法逻辑上符合你对该类的长度的设定,否则你无法利用它得到一个有意义的长度表征。

值得一提的是,Python的内置函数len就是隐式调用类的__len__方法:


class A():
     def __len__(self):
         print('__len__ method in A is called')
         return 250
 
 a = A()
 print(len(a))
 
 '''
 执行结果:
 __len__ method in A is called
 250
 '''
 
 class B():
     # 没有定义__len__函数
     pass
 
 b = B()
 print(len(B))
 
 '''
 执行结果:
 AttributeError: B instance has
 no attribute '__len__'
 '''


真值测试的运行机制

了解了两种魔术方法后,我们对真值测试的流程做一个介绍。假设我们要对一个Python对象进行真值测试:

  1. 如果该对象定义了__bool__方法,则依__bool__方法返回的真值作为该对象的真值。
  2. 否则,如果该对象定义了__len__方法,则根据__len__方法返回的整数进行判断,返回值为0则真值为False,不为0则真值为True
  3. 否则,该对象的真值为True

下面通过代码向大家直观展示:


class A():
     def __bool__(self):
         print('__bool__ is called')
         return True
     def __len__(self):
         print('__len__ is called')
         return 0
 
 a = A()
 if a:
     print('a is True')
 else:
     print('a is False')
 
 '''
 执行结果:
 __bool__ is called
 a is True
 '''


上述代码中,类A同时定义了__bool____len__方法,但根据真值测试的优先级,优先采用__bool__作为判断依据,__len__并没有被调用。


class B():
     def __len__(self):
         print('__len__ is called')
         return 0
 
 b = B()
 if b:
     print('b is True')
 else:
     print('b is False')
 
 '''
 执行结果:
 __len__ is called
 b is False
 '''


上述代码中,类B只定义了__len__方法,则调用__len__进行真值测试,由于该方法返回值为0,对象b被判断为False


class C():
     pass
 
 c = C()
 if c:
     print('c is True')
 else:
     print('c is False')
 
 '''
 执行结果:
 c is True
 '''


上述代码中,类C没有定义__bool____len__函数,则默认返回True


>>> int(3).__bool__()
 True
 >>> int(0).__bool__()
 False
 >>> list().__bool__()
 Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
 AttributeError: 'list' object has no attribute '__bool__'
 >>> list().__len__()
 0


上述代码则展示了内置数据类型intlist的真值测试,其中int是定义了__bool__函数的,list没有定义__bool__但定义了__len__,到这里我们也能理解为什么空列表会被判断成False了。

内置函数bool()

Python有一个内置函数bool(),它接受一个对象作为参数,返回的就是该对象在Python下进行真值测试的结果,即该函数返回一个bool变量,该变量的值的判断方法与上面介绍的真值测试完全一致


if a:
     print('a is True')
 else:
     print('a is False')
 
 # 等价于
 x = bool(a)
 if x is True:
     print('a is True')
 else:
     print('a is False')


总结

Python提供了一个简单有效的真值测试机制,来得到一切对象的隐式布尔值。该机制借助两个魔术方法__bool____len__,如果我们希望给自定义的类赋予符合一定逻辑的布尔值,可以通过定义这两个魔术方法来完成。Python也提供了一个内置函数bool,使得对象的隐式布尔值可以通过该函数显式计算得到,而不一定只能用于ifwhile等过程。