CPython C API 布尔对象详解:Py_True / Py_False 单例、immortal 语义与 PyBool_FromLong
CPython 的 C 扩展开发中,bool 类型是唯一"不需要创建和销毁"的对象类型:整个运行时只存在 Py_False 和 Py_True 两个布尔对象,且自 3.12 起它们都是不可回收的 immortal(永生)对象。本篇以官方文档 Doc/c-api/bool.rst 为核心脉络,逐个讲解 PyBool_Type、PyBool_Check、Py_False/Py_True、Py_RETURN_FALSE/Py_RETURN_TRUE、PyBool_FromLong 等 API 的语义与版本变化,并结合 Objects/boolobject.c 与 Include/boolobject.h 的源码实现,说明单例是如何构造、保护以及在 C 扩展函数中正确返回的。
为什么布尔类型"不适用常规的创建与销毁"
官方文档的开篇定调了整章的核心事实:Python 中的布尔值在 C 层实现为整数(int)的子类,且全局只存在两个布尔对象 Py_False 和 Py_True。因此针对其他类型常规的"创建/删除"函数(如 PyLong_FromLong 会返回一个新对象)对布尔值并不适用——你不能"新建"一个 True,只能拿到那唯一的一个。
这一设计可以直接从源码得到印证。Objects/boolobject.c 中 PyBool_FromLong 的实现就是一行三元表达式:
PyObject *PyBool_FromLong(long ok)
{
return ok ? Py_True : Py_False;
}
它不分配任何内存,只是按 v 的真值性在两个单例之间做选择。同样,构造器 bool_new(Objects/boolobject.c)无论传入什么参数,最终也是通过 PyBool_FromLong(PyObject_IsTrue(x)) 返回这两个单例之一,这与 Python 层 bool(1) is bool(2) is True 的语义一致。
PyBool_Type 类型对象的定义也印证了"bool 是 int 的子类"这一前提:PyBool_Type 结构体中 tp_base 指向 &PyLong_Type(Objects/boolobject.c),并且类型文档字符串明确写着 "The class bool is a subclass of the class int, and cannot be subclassed"(Objects/boolobject.c)。测试用例 Lib/test/test_bool.py 的 test_subclass 专门验证了 class C(bool): pass 会抛出 TypeError,即 bool 不可再被继承。
核心 API 逐项解析
PyBool_Type:布尔类型的 PyTypeObject
PyBool_Type 是表示 Python 布尔类型的 PyTypeObject 实例,与 Python 层的 bool 类是同一个对象。它在稳定 ABI 中的声明位于 Include/object.h:
PyAPI_DATA(PyTypeObject) PyBool_Type;
注意 Include/boolobject.h 中的注释 "PyBool_Type is declared by object.h"——布尔头文件本身不再重复声明它。根据 Misc/stable_abi.toml 的 [data.PyBool_Type] 条目,PyBool_Type 自 Python 3.2 起就包含在稳定 ABI 中,可以在 limited C API 扩展中安全使用。
Py_False 与 Py_True:唯二的布尔单例
文档声明 Py_False 是 Python 的 False 对象,Py_True 是 True 对象;两者都没有方法,并且是 immortal(永生对象,永不回收)。版本标注为:
- versionchanged 3.12:
Py_False是 immortal 对象。 - versionchanged 3.12:
Py_True是 immortal 对象。
"immortal" 的含义可以结合实现来看。两个单例在 C 层实际是以 PyLongObject 结构体静态定义的(Include/boolobject.h 中的内部符号 _Py_FalseStruct / _Py_TrueStruct):
// Include/boolobject.h
/* Don't use these directly */
PyAPI_DATA(PyLongObject) _Py_FalseStruct;
PyAPI_DATA(PyLongObject) _Py_TrueStruct;
它们在 Objects/boolobject.c 中初始化,带有一个特殊的 long 标签:
struct _longobject _Py_FalseStruct = {
PyObject_HEAD_INIT(&PyBool_Type)
{ .lv_tag = _PyLong_FALSE_TAG,
{ 0 }
}
};
struct _longobject _Py_TrueStruct = {
PyObject_HEAD_INIT(&PyBool_Type)
{ .lv_tag = _PyLong_TRUE_TAG,
{ 1 }
}
};
lv_tag 使用 _PyLong_FALSE_TAG / _PyLong_TRUE_TAG 而非普通小整数的标签,这解释了为什么布尔值虽然"长得像"小整数 0/1,却不会与 int 的小整数单例混淆——int(False) 返回的是 0 这个 int 对象而不是 False(测试 Lib/test/test_bool.py 的 test_int 用 assertIsNot(int(False), False) 精确验证了这一点)。
immortal 的运行时保护体现在析构函数上:bool_dealloc 正常情况下永远不会被调用;但万一引用计数被误减到零,它也不会让单例真正消失,而是把对象重新置为 immortal(Objects/boolobject.c):
static void
bool_dealloc(PyObject *boolean)
{
/* This should never get called, but we also don't want to SEGV if
* we accidentally decref Booleans out of existence. Instead,
* since bools are immortal, re-set the reference count.
*/
_Py_SetImmortal(boolean);
}
对 C 扩展作者的直接推论是:Py_True / Py_False 不需要 Py_INCREF/Py_DECREF 管理,可以无限次地"持有"和返回。
一个容易踩坑的细节是 limited C API 版本差异。Include/boolobject.h 中 Py_False / Py_True 宏本身按 Py_LIMITED_API 分两路:
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030D0000
# define Py_False Py_GetConstantBorrowed(Py_CONSTANT_FALSE)
# define Py_True Py_GetConstantBorrowed(Py_CONSTANT_TRUE)
#else
# define Py_False _PyObject_CAST(&_Py_FalseStruct)
# define Py_True _PyObject_CAST(&_Py_TrueStruct)
#endif
即在使用 3.13+ limited API 时,通过 Py_GetConstantBorrowed 以借用引用方式取常量;旧路径则直接取内部结构体地址。两条路径拿到的都是同一个单例,语义不变。
PyBool_Check:类型判断
文档给出的 PyBool_Check(PyObject *o) 语义是:当 o 的类型恰好是 PyBool_Type 时返回真,该函数"总是成功"(不会设置异常,也不会失败)。注意它是精确类型检查而非 isinstance 检查——但由于 bool 不可被子类化,精确检查在这里与实例检查等价。
它的实际定义在 Include/boolobject.h 中是一个内联宏:
#define PyBool_Check(x) Py_IS_TYPE((x), &PyBool_Type)
判断的是 Py_TYPE(o) == &PyBool_Type,因此不会跟随继承链,也不会在参数为 NULL 之外做额外防御。Misc/stable_abi.toml 中未将 PyBool_Check 单列为 ABI 符号(它只是宏),宏依赖的 PyBool_Type 数据符号才是稳定 ABI 的一部分。
PyBool_FromLong:从 C long 得到布尔对象
PyObject *PyBool_FromLong(long v) 返回 Py_True 或 Py_False,取决于 v 的真值——即 v != 0 时返回 Py_True,否则返回 Py_False。源码实现见 Objects/boolobject.c。使用要点:
- 返回的是单例引用,调用者拿到的是新引用(作为函数返回值的一部分),但因为它 immortal,后续引用计数处理与普通对象相同即可,永远不会因它而崩溃;
- 参数是 C 的
long,所以传入其他 C 整数类型时按常规隐式转换即可; - 在 Misc/stable_abi.toml 的
[function.PyBool_FromLong]条目中,它自 3.2 起就是稳定 ABI 函数,limited C API 扩展可以直接使用。
Py_RETURN_TRUE 与 Py_RETURN_FALSE
文档对这两个宏的描述很简短:Py_RETURN_FALSE 从函数返回 Py_False,Py_RETURN_TRUE 从函数返回 Py_True。它们是最常用的 C 扩展惯用法,典型场景如 C 回调需要返回一个布尔结果:
static PyObject *
my_is_ready(PyObject *self, PyObject *args)
{
if (some_condition()) {
Py_RETURN_TRUE;
}
Py_RETURN_FALSE;
}
宏的具体展开与版本强相关,Include/boolobject.h 给出了完整定义:
/* Macros for returning Py_True or Py_False, respectively.
* Only treat Py_True and Py_False as immortal in the limited C API 3.12
* and newer. */
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x030c0000
# define Py_RETURN_TRUE return Py_NewRef(Py_True)
# define Py_RETURN_FALSE return Py_NewRef(Py_False)
#else
# define Py_RETURN_TRUE return Py_True
# define Py_RETURN_FALSE return Py_False
#endif
这正是文档中 "versionchanged 3.12:Py_True/Py_False is immortal" 在 API 层的具体落地:在 3.12+(或完整 C API)下,return Py_True 直接返回单例即可,无需增加引用计数;而在针对 3.11 及更早的 limited API 编译时,宏会自动展开为 return Py_NewRef(Py_True),为返回值增加一个引用,保持"函数返回新引用"的契约。对扩展作者来说这意味着:跨版本代码统一使用 Py_RETURN_TRUE/Py_RETURN_FALSE 宏即可,无需关心 immortal 语义在哪个版本生效,宏替你处理了差异。
单例之外的两个实用判断:Py_IsTrue 与 Py_IsFalse
虽然 Doc/c-api/bool.rst 原文档没有展开这两个宏,但作为布尔对象头文件的一部分,从源码结构看它们与 Py_False/Py_True 单例语义直接相关,值得了解。Include/boolobject.h 定义:
// Test if an object is the True singleton, the same as "x is True" in Python.
PyAPI_FUNC(int) Py_IsTrue(PyObject *x);
#define Py_IsTrue(x) Py_Is((x), Py_True)
// Test if an object is the False singleton, the same as "x is False" in Python.
PyAPI_FUNC(int) Py_IsFalse(PyObject *x);
#define Py_IsFalse(x) Py_Is((x), Py_False)
注意语义边界:Py_IsTrue(x) 判断的是 x is True(同一性),与 PyObject_IsTrue(x)(判断真值,x 可以是 0、None、空列表等)完全不同。在 C 扩展里做 "参数必须严格是布尔单例" 的校验时,应使用 Py_IsTrue/Py_IsFalse 或 PyBool_Check,而 PyObject_IsTrue 适合做"真值求值"。
bool 类型的数值行为:源码中的覆写细节
PyBool_Type 虽然继承自 PyLong_Type,但在 Objects/boolobject.c 中专门覆写了部分位运算,保证"两边都是 bool 时结果仍是 bool":
bool_and(&)、bool_or(|)、bool_xor(^):若任一操作数不是PyBool_Check通过,则委托给PyLong_Type的对应槽位,结果退化为int;两个都是 bool 时,结果经由PyBool_FromLong返回 bool(Objects/boolobject.c);bool_invert(~):对 bool 做按位取反会触发DeprecationWarning,提示该操作在 Python 3.16 将被移除,语义上是"底层 int 对象的按位取反",通常不是期望的布尔取反;推荐用not或~int(x)(Objects/boolobject.c)。
repr 也是专门实现的:bool_repr 直接返回 True/False 这两个 interned 字符串对象,保证 repr(False) == 'False' 且 eval(repr(False)) is False(Objects/boolobject.c,对应测试 Lib/test/test_bool.py 的 test_repr)。
在扩展模块中正确使用布尔 API 的完整示例
综合以上 API,下面是一个最小可运行的 C 扩展片段,展示"接收 C 层计算结果并以布尔对象返回、并用类型宏做参数校验"的标准写法:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
/* 判断参数是否严格为布尔单例(等价于 Python 的 isinstance(x, bool)
* ——由于 bool 不可被子类化,精确类型检查即可) */
static int
require_bool(PyObject *x, const char *name)
{
if (!PyBool_Check(x)) {
PyErr_Format(PyExc_TypeError,
"%s must be bool, got %.200s", name, Py_TYPE(x)->tp_name);
return 0;
}
return 1;
}
static PyObject *
check_flag(PyObject *self, PyObject *arg)
{
if (!require_bool(arg, "flag")) {
return NULL;
}
long truthy = PyObject_IsTrue(arg); /* 判断真值 */
if (truthy < 0) {
return NULL;
}
return PyBool_FromLong(truthy); /* 返回 Py_True / Py_False 单例 */
}
static PyMethodDef demo_methods[] = {
{"check_flag", check_flag, METH_O, "Return the given bool as-is."},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef demo_module = {
PyModuleDef_HEAD_INIT, "demo_bool", NULL, -1, demo_methods
};
PyMODINIT_FUNC
PyInit_demo_bool(void)
{
return PyModule_Create(&demo_module);
}
要点回顾:参数校验用 PyBool_Check(精确匹配 PyBool_Type),真值求值用 PyObject_IsTrue(可能返回 -1 表示出错,需检查),返回布尔用 PyBool_FromLong 或 Py_RETURN_TRUE/Py_RETURN_FALSE 宏;全程不需要对 Py_True/Py_False 做任何显式的引用计数管理。
稳定 ABI 与可依赖的版本边界
从 Misc/stable_abi.toml 可以确认本主题各符号的 ABI 状态:
[function.PyBool_FromLong]:3.2 加入稳定 ABI,limited C API 可用;[data.PyBool_Type]:3.2 加入稳定 ABI;[data._Py_FalseStruct]/[data._Py_TrueStruct]:3.2 加入,但标记abi_only = true,即仅供 CPython 内部实现使用,扩展代码不应直接引用(头文件注释也明确 "Don't use these directly");Py_False/Py_True宏以及Py_RETURN_TRUE/Py_RETURN_FALSE是编译期宏,不参与 ABI 导出,但其展开结果依赖的符号均在稳定 ABI 内。
需要强调的适用前提:immortal 语义(以及 Py_RETURN_* 不再增加引用计数的展开形式)以 Python 3.12 为界,文档中的两处 versionchanged 3.12 标注是编写跨版本扩展时必须对照的点;如果你的扩展通过 Py_LIMITED_API 固定到 3.11 或更早,宏会自动切换为 Py_NewRef 版本,行为依旧正确。
小结与延伸阅读
CPython 布尔对象 API 的全部要点可归纳为:
- 单例模型:
Py_True/Py_False是仅有的两个布尔对象,静态定义在PyLongObject结构体中并带独立 long 标签,自 3.12 起为 immortal; - 判断与构造:
PyBool_Check做精确类型匹配,PyBool_FromLong按真值返回单例,Py_IsTrue/Py_IsFalse做is True/is False同一性判断; - 返回惯例:C 扩展函数返回布尔时优先使用
Py_RETURN_TRUE/Py_RETURN_FALSE,引用计数语义由宏按 API 版本自适应; - 版本边界:
PyBool_Type与PyBool_FromLong自 3.2 起属于稳定 ABI,limited C API 扩展可安全依赖。
进一步阅读建议:Include/boolobject.h 查看全部宏与函数声明;Objects/boolobject.c 查看类型对象、单例结构与数值槽位覆写的完整实现;Lib/test/test_bool.py 查看 PEP 285 对 bool 行为约定的完整测试集;以及 C API 章节索引 Doc/c-api/index.rst 中相邻的 long(Doc/c-api/long.rst)与对象/引用计数(Doc/c-api/refcounting.rst)文档,它们与布尔单例的 immortal 语义直接相关。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0625
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00