在开发 Python3 项目的过程中经常会遇到需要判断实例真假的情况,那么究竟哪些实例会被认为是真,哪些又被认为是假呢?
常言道,去伪存真,除去所有的假,剩下的就都是真了。
Python3 中判断为假的值有以下几种:
- False
- 所有数值类型的零值
- 空序列空集合空映射
- 定义了
__bool__
或__len__
并返回为 False 或 0 的用户自定义类的实例 - None
False自不必说,名字就出卖了自己的内在,接下来我们就来谈谈其余的这些假值。
数值类型
Python3 内置的数值类型有三种: 整数(int),浮点小数(float),和复数(complex)。另外,在标准库 fractions 和 decimal 中还分别包含了另外两种: 分数(Fraction) ,浮点小数威力加强版(Decimal)。
它们的零值分别长这样 0, 0.0, 0j, Fraction(0, 1), Decimal(0)
(分数零与数学定义一致)。以上五个值均被判断为 False。
Python 内置的 bool(x) 函数可以返回 x 的真值,我们可通过它来验证这些零值是否真的如我所说,全部返回 False。
>>> bool(0)
False
>>> bool(134)
True
>>> bool(-1)
True
>>> bool(0.0)
False
>>> bool(0.0000001)
True
>>> bool(-0.0000000001)
True
>>> bool(0j)
False
>>> bool(1j)
True
>>> bool(-1j)
True
>>> from fractions import Fraction
>>> bool(Fraction(0, 1))
False
>>> bool(Fraction(0, -1))
False
>>> bool(Fraction(0, 1000))
False
>>> bool(Fraction(1, 1000))
True
>>> from decimal import Decimal
>>> bool(Decimal(0))
False
>>> bool(Decimal(1.234))
True
>>> bool(Decimal(0.0000001))
True
>>> bool(Decimal(-0.0000001))
True
>>>
实践证明,我说滴么错~接下来空序列空集合空映射。
序列集合映射
序列
Python 序列有四种: 字符串(str),列表(list),元组(tuple)和范围(range)。
str, list
str, list 没的说,空值即为假,其余值为真。
>>> bool('')
False
>>> bool('0')
True
>>> bool([])
False
>>> bool([0])
True
这里特别说明一下 tuple 和 range。
tuple
对于 tuple 来说,如果某个 tuple 内部只有一个元素,那么它会认为自己不是 tuple 类型,而转化为其内部元素的类型。
>>> a = (1)
>>> a
1
>>> type(a)
<class 'int'>
>>> b = ('hello')
>>> b
'hello'
>>> type(b)
<class 'str'>
可爱的一点是,如果某个 tuple 内部一个元素都没有,那么它会认为自己还是 tuple 类型。
>>> type(())
<class 'tuple'>
所以,如果判断 (0)
或者 (((0)))
或者 0 外面更多层 ()
的真值,本质还是在判断 0
的真值,也就是 False。()括住其他假值与 0 同理。
range
range 也比较奇特,不仅 range(0) 被认为假, 只要是范围小于等于 0, 都会被认为是假。
>>> bool(range(0))
False
>>> bool(range(0, -1))
False
>>> bool(range(10, 6))
False
>>> bool(range(0, 1))
True
映射和集合
Python3 中标准映射类型(standard mapping type)只有字典(dictionary)一种,集合类型有 set 和 frozenset 两种。
同样,也是空值为假,其余为真。
他们的空值分别为 {}, set(), frozenset()
>>> bool({})
False
>>> bool({'a': 1})
True
>>> bool(set())
False
>>> bool({0})
True
>>> bool(frozenset())
False
>>> bool(frozenset([1]))
True
看看测试结果,没毛病~
多提一句,区分一下 {}, {0}, {'a': 0}
{} 是一个空字典,{0} 是一个包含一个元素的集合,{'a': 0} 是包含一个键值对的字典。
说白了就是:
- dict 和 set 都能用 {} 初始化
- {} 表示 dict
- set() 表示空集合
- {} 中有 key: value 就是字典
- {} 中只有 value 没有 key 类型就是 set
判断依据
看到这里的同学,肯定会有这样的疑惑,这些真真假假到底是根据什么判断的呢?
判断依据其实有两个,一个是 __bool__
另一个是 __len__
。
这俩货是 Python3 的两个魔术方法,内置的类很多都会实现其中的一个,这两个方法针对该类不同的实例会有不同的返回值,Python3 会根据它们的返回值,来判断实例的真值。
实例的 __bool__
或 __len__
方法的返回值为 False 或 0,则该实例为假,
实例的 __bool__
或 __len__
方法的返回值为 True 或 正整数,则该实例为真,
__bool__
比如数值类型们,它们实现的是 __bool__
方法,针对零值的实例,__bool__
会返回 False,其他的返回 True。
>>> a = 5
>>> a.__bool__()
True
>>> b = 0
>>> b.__bool__()
False
顺便说一句,bool 类型其实是 int 类型的子类。
>>> isinstance(False, int)
True
__len__
再如序列集合映射,它们实现的是 __len__
方法,针对空序列空映射空集合,__len__
会返回 0,其他的会返回自身元素个数。
之前提到的 range 也是差不多的道理,只不过当范围区间为空集时,它的 __len__
就会返回 0,而不会出现负数的情况。这一点可以想想数学中的数轴区间集合啥的。
>>> ''.__len__()
0
>>> ().__len__()
0
>>> [].__len__()
0
>>> range(0).__len__()
0
>>> {}.__len__()
0
>>> set().__len__()
0
>>> frozenset().__len__()
0
>>> '0'.__len__()
1
>>> range(1).__len__()
1
>>> range(3, 1).__len__()
0
自定义类
了解了 __bool__
和 __len__
就可以说一下自定义类的真值了。
如果用户自定义的类实现了 __bool__
和 __len__
二者其一,那么 Python3 会像对待内置类型那样,就根据 __bool__
或 __len__
的返回值来判断该用户类实例的真值。
注意,用户自定义这两个方法时,需要注意返回类型。
__bool__
方法只能返回 True 或者 False。
__len__
方法只能返回非负整数。
>>> class Test(object):
id = 0
def __init__(self, id):
self.id = id
def __bool__(self):
print('__bool__ method called')
return self.id > 0
>>> bool(Test(1))
__bool__ method called
True
>>> bool(Test(0))
__bool__ method called
False
>>> bool(Test(-1))
__bool__ method called
False
>>> class Test(object):
length = 0
def __init__(self, length):
self.length = length
def __len__(self):
print('__len__ method called')
return int(self.length) if int(self.length) > 0 else 0
>>> bool(Test(1))
__len__ method called
1
>>> bool(Test(0))
__len__ method called
0
>>> bool(Test(-1))
__len__ method called
0
如果用户自定义的类将 __bool__
和 __len__
都实现了,那么 Python3 会优先根据 __bool__
的返回值来判断该用户类实例的真值。
>>> class Test(object):
id = 0
length = 0
def __init__(self, id, length):
self.id = id
self.length = length
def __bool__(self):
print('__bool__ method called')
return self.id > 0
def __len__(self):
print('__len__ method called')
return int(self.length) if int(self.length) > 0 else 0
>>> bool(Test(id=1, length=-5))
__bool__ method called
True
>>> bool(Test(id=0, length=5)))
__bool__ method called
False
如果用户自定义的类中,这两个魔术方法都没有实现,那么这个类所有的实例都会被认为是 True。
>>> class Test(object):
id = 0
def __init__(self, id):
self.id = id
>>> bool(Test(142857))
True
>>> bool(Test(0)))
True
None
内置类 NoneType 实现了 __bool__
方法,None 是 NoneType 类唯一的实例,它的 __bool__
返回值是 False,所以它的真值为 False。
>>> type(None)
<class 'NoneType'>
>>> None.__bool__()
False
>>> bool(None)
False
总结
- Python3 所有类的实例的真值由所属类实现的
__bool__
或__len__
方法的返回值决定,优先级__bool__
>__len__
, 若二者均未实现,则默认为 True __bool__
返回 True,实例为 True,__bool__
返回 False ,实例为 False__bool__
未实现时,__len__
返回 > 0 整数,实例为 True,__len__
返回 0 ,实例为 False- Python3 中内置类会实现
__bool__
和__len__
其中之一。 - 用户自定义类也可以实现
__bool__
和__len__
, 但返回值需遵从 6. __bool__
返回值只能是 bool 类型,__len__
的返回值只能是非负整数