Python3中的真真假假

302 阅读6分钟

在开发 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

总结

  1. Python3 所有类的实例的真值由所属类实现的 __bool____len__ 方法的返回值决定,优先级 __bool__ > __len__, 若二者均未实现,则默认为 True
  2. __bool__ 返回 True,实例为 True,__bool__ 返回 False ,实例为 False
  3. __bool__ 未实现时,__len__ 返回 > 0 整数,实例为 True,__len__ 返回 0 ,实例为 False
  4. Python3 中内置类会实现 __bool____len__ 其中之一。
  5. 用户自定义类也可以实现 __bool____len__ , 但返回值需遵从 6.
  6. __bool__ 返回值只能是 bool 类型,__len__ 的返回值只能是非负整数