为什么CPython返回True/False单例新指针并增加其引用计数?
Python布尔单例的引用计数困惑解答
布尔类型的单例特性
Python中的布尔类型是整数的子类,仅存在Py_False和Py_True两个布尔对象。
Py_False和Py_True分别对应Python层面False和True的单例,你可以通过以下代码验证:
a = True b = True a is b # 返回True
False的表现完全一致。
CAPI中的布尔对象处理
在Python的C实现中,PyBool_FromLong方法负责从long类型创建布尔对象,代码如下:
PyObject *PyBool_FromLong(long ok) { PyObject *result; if (ok) result = Py_True; else result = Py_False; return Py_NewRef(result); }
它会返回指向对应单例的指针,但返回前会调用Py_NewRef增加对象的引用计数。
同时,Py_True和Py_False的定义也明确标注了规则:
/* Py_False and Py_True are the only two bools in existence. Don't forget to apply Py_INCREF() when returning either!!! */ /* Don't use these directly */ PyAPI_DATA(PyLongObject) _Py_FalseStruct; PyAPI_DATA(PyLongObject) _Py_TrueStruct; /* Use these macros */ #define Py_False _PyObject_CAST(&_Py_FalseStruct) #define Py_True _PyObject_CAST(&_Py_TrueStruct)
注释强制要求返回这两个对象时必须增加引用计数,PyBool_FromLong也严格遵循了这个要求。
核心疑问与解答
疑问:既然是不会被垃圾回收的单例,为何需要维护引用计数?
这本质是Python C API统一的内存管理规则,和对象是否为单例无关:
- C API的硬性约定是:任何通过API返回的对象引用,都必须是一个“调用者拥有的引用”——调用者需要负责在不再使用时调用
Py_DECREF释放引用。 - 哪怕是单例对象,也得遵守这个规则。如果返回时不增加引用计数,调用者后续调用
Py_DECREF会导致引用计数变为负数,触发内存错误或破坏解释器的引用计数系统。 - 单例的“不会被回收”是因为它们的初始引用计数被设置为极大值(或解释器做了特殊保护),但日常的引用增减仍要遵循通用规则,保证整个引用计数系统的一致性。
- 举个实际场景:如果某个函数返回Py_True时不递增引用计数,调用者拿到引用后调用
Py_DECREF,会让Py_True的引用计数错误减少,虽然不会真的被回收,但会破坏引用计数的准确性,可能导致其他依赖该计数的逻辑出现异常。
内容的提问来源于stack exchange,提问作者EJoshuaS
相关产品推荐
相关产品推荐

