|
| 1 | +# Python 布尔与 None 对象 |
| 2 | + |
| 3 | +`True`、`False`、`None` 是我们每天都在用、却很少深究的几个值。它们有个共同点:都是**单例**——全局只有一个对象。这一章我们就来看它们的实现,并顺势把贯穿全书的「用一个对象代替无数等价对象」这个省内存手法串起来。 |
| 4 | + |
| 5 | +```python |
| 6 | +>>> True is True, None is None |
| 7 | +(True, True) |
| 8 | +>>> True == 1, isinstance(True, int) |
| 9 | +(True, True) |
| 10 | +``` |
| 11 | + |
| 12 | +第二行也许让你意外:`True` 居然等于 `1`、还是 `int` 的实例?这正是布尔对象最有意思的地方。 |
| 13 | + |
| 14 | +## 布尔对象:是 int 的子类 |
| 15 | + |
| 16 | +在 CPython 里,`bool` 不是独立的类型,而是 `int` 的**子类**——它的类型对象 `PyBool_Type` 把 `tp_base` 指向了 `PyLong_Type`: |
| 17 | + |
| 18 | +`源文件:`[Objects/boolobject.c](https://github.com/python/cpython/blob/v3.7.0/Objects/boolobject.c#L134) |
| 19 | + |
| 20 | +```c |
| 21 | +// Objects/boolobject.c |
| 22 | +PyTypeObject PyBool_Type = { |
| 23 | + ...... |
| 24 | + "bool", /* tp_name */ |
| 25 | + ...... |
| 26 | + &PyLong_Type, /* tp_base */ // 父类是 int |
| 27 | + ...... |
| 28 | + bool_new, /* tp_new */ |
| 29 | +}; |
| 30 | +``` |
| 31 | + |
| 32 | +而 `True` 和 `False` 本身,就是两个值为 1 和 0 的**整数对象**(`struct _longobject`),只不过类型被设成了 `bool`: |
| 33 | + |
| 34 | +`源文件:`[Objects/boolobject.c](https://github.com/python/cpython/blob/v3.7.0/Objects/boolobject.c#L177) |
| 35 | + |
| 36 | +```c |
| 37 | +// Objects/boolobject.c |
| 38 | +struct _longobject _Py_FalseStruct = { |
| 39 | + PyVarObject_HEAD_INIT(&PyBool_Type, 0) // 类型 bool,值 0 |
| 40 | + { 0 } |
| 41 | +}; |
| 42 | +struct _longobject _Py_TrueStruct = { |
| 43 | + PyVarObject_HEAD_INIT(&PyBool_Type, 1) // 类型 bool,值 1 |
| 44 | + { 1 } |
| 45 | +}; |
| 46 | +``` |
| 47 | + |
| 48 | + |
| 49 | + |
| 50 | +所以布尔值在数值上下文里就是 1 和 0,能直接参与运算: |
| 51 | + |
| 52 | +```python |
| 53 | +>>> type(True), bool.__bases__ |
| 54 | +(<class 'bool'>, (<class 'int'>,)) |
| 55 | +>>> True + True, True * 3 |
| 56 | +(2, 3) |
| 57 | +>>> sum([True, True, False, True]) # 统计 True 的个数,常用技巧 |
| 58 | +3 |
| 59 | +``` |
| 60 | + |
| 61 | +`sum([...])` 这个「数 `True` 的个数」是很常见的写法,背后正是因为 `True` 就是整数 1。 |
| 62 | + |
| 63 | +## True 与 False 是单例 |
| 64 | + |
| 65 | +布尔类型永远只有两个对象。看 `bool(x)` 的实现——它不新建对象,只返回 `Py_True` 或 `Py_False` 这两个现成的单例: |
| 66 | + |
| 67 | +`源文件:`[Objects/boolobject.c](https://github.com/python/cpython/blob/v3.7.0/Objects/boolobject.c#L40) |
| 68 | + |
| 69 | +```c |
| 70 | +// Objects/boolobject.c |
| 71 | +/* We define bool_new to always return either Py_True or Py_False */ |
| 72 | +static PyObject * |
| 73 | +bool_new(PyTypeObject *type, PyObject *args, PyObject *kwds) |
| 74 | +{ |
| 75 | + ...... |
| 76 | + return PyBool_FromLong(ok); // 只会返回 Py_True / Py_False |
| 77 | +} |
| 78 | +``` |
| 79 | +
|
| 80 | +```python |
| 81 | +>>> bool(5) is True, bool(0) is False |
| 82 | +(True, True) |
| 83 | +>>> bool(5) is bool(42) # 任何真值 bool() 出来都是同一个 True |
| 84 | +True |
| 85 | +``` |
| 86 | + |
| 87 | +正因为只有两个单例,判断真假时直接写 `if x:` 即可,不必(也不该)写 `if x == True:`。 |
| 88 | + |
| 89 | +## None 对象 |
| 90 | + |
| 91 | +`None` 是 `NoneType` 类型唯一的实例,同样是一个全局单例: |
| 92 | + |
| 93 | +`源文件:`[Objects/object.c](https://github.com/python/cpython/blob/v3.7.0/Objects/object.c#L1607) |
| 94 | + |
| 95 | +```c |
| 96 | +// Objects/object.c |
| 97 | +PyObject _Py_NoneStruct = { |
| 98 | + _PyObject_EXTRA_INIT |
| 99 | + 1, &_PyNone_Type // 引用计数 1,类型 NoneType |
| 100 | +}; |
| 101 | +``` |
| 102 | + |
| 103 | +`Py_None` 这个宏就指向它,全局只此一个。它甚至**永远不会被销毁**——它的析构函数被设计成直接报致命错误: |
| 104 | + |
| 105 | +`源文件:`[Objects/object.c](https://github.com/python/cpython/blob/v3.7.0/Objects/object.c#L1505) |
| 106 | + |
| 107 | +```c |
| 108 | +// Objects/object.c |
| 109 | +static void |
| 110 | +none_dealloc(PyObject* ignore) |
| 111 | +{ |
| 112 | + /* This should never get called ... */ |
| 113 | + Py_FatalError("deallocating None"); // 万一 None 被销毁,直接致命错误 |
| 114 | +} |
| 115 | +``` |
| 116 | +
|
| 117 | +```python |
| 118 | +>>> None is None |
| 119 | +True |
| 120 | +>>> type(None) |
| 121 | +<class 'NoneType'> |
| 122 | +>>> type(None)() is None # 连 NoneType() 也只返回同一个 None |
| 123 | +True |
| 124 | +``` |
| 125 | + |
| 126 | +正因为 `None` 是单例,判断一个值是不是 `None`,应该用 **`is None`** 而不是 `== None`——`is` 直接比指针,又快又不会被自定义的 `__eq__` 干扰。 |
| 127 | + |
| 128 | +## 其它单例:Ellipsis 与 NotImplemented |
| 129 | + |
| 130 | +除了 `None`,CPython 还有两个内建单例: |
| 131 | + |
| 132 | +- **`Ellipsis`**(写作 `...`):占位用的单例,常见于 numpy 的多维切片、类型注解等。 |
| 133 | +- **`NotImplemented`**:运算符方法(如 `__eq__`、`__add__`)在「我处理不了这种类型」时返回它,提示解释器去尝试对方的反向方法。 |
| 134 | + |
| 135 | + |
| 136 | + |
| 137 | +```python |
| 138 | +>>> ... is Ellipsis, type(...) |
| 139 | +(True, <class 'ellipsis'>) |
| 140 | +>>> NotImplemented |
| 141 | +NotImplemented |
| 142 | +``` |
| 143 | + |
| 144 | +> 注意 `NotImplemented`(运算符回退用的单例)和 `NotImplementedError`(一个异常类)是两回事,别混用。 |
| 145 | +
|
| 146 | +## 单例与缓存:一个贯穿全书的模式 |
| 147 | + |
| 148 | +把视野拉开,你会发现 `None`、`True`、`False` 只是一个更大模式的特例——CPython 反复在用同一招:**用一个对象代替无数个等价的对象**。前面各章其实都见过它: |
| 149 | + |
| 150 | +| 机制 | 谁来代替谁 | 出处 | |
| 151 | +|---|---|---| |
| 152 | +| 小整数对象池 | `[-5, 257)` 内的整数共享同一对象 | 整数对象 | |
| 153 | +| 字符串驻留 | 等值的标识符字符串共享同一对象 | 字符串对象 | |
| 154 | +| 空元组单例 | 所有 `()` 是同一个对象 | 元组对象 | |
| 155 | +| 浮点 free list | 复用最多 100 个已释放的浮点对象 | 浮点数对象 | |
| 156 | +| `None` / `True` / `False` | 各自全局唯一的单例 | 本章 | |
| 157 | + |
| 158 | + |
| 159 | + |
| 160 | +这套手法的共同好处有两个:**省内存**(不为等价的值重复分配),以及**比较更快**(同一对象可以直接用 `is` 比指针,而不必逐字段比较)。理解了这一点,再看 `a is b` 在不同对象上时而成立、时而不成立,就不会困惑了——成立与否,取决于该值有没有被「单例化/缓存」。 |
| 161 | + |
| 162 | +--- |
| 163 | + |
| 164 | +小结一下: |
| 165 | + |
| 166 | +- `bool` 是 `int` 的**子类**,`True`/`False` 就是值为 1/0 的整数对象(类型为 `bool`),所以能参与数值运算(`sum(bools)` 数个数); |
| 167 | +- `True`/`False`/`None` 都是**单例**,`bool(x)`、`NoneType()` 都只返回现成的单例对象,判断时用 `if x:` 和 `is None`; |
| 168 | +- `None` 永不销毁(`none_dealloc` 会触发致命错误);另有 `Ellipsis`、`NotImplemented` 两个内建单例; |
| 169 | +- 这些单例是 CPython「**用一个对象代替无数等价对象**」这一省内存模式的体现,和小整数池、字符串驻留、空元组、浮点 free list 一脉相承——既省内存,又让 `is` 比较更快。 |
0 commit comments