首页
/ CPython C API 布尔对象详解:Py_True / Py_False 单例、immortal 语义与 PyBool_FromLong

CPython C API 布尔对象详解:Py_True / Py_False 单例、immortal 语义与 PyBool_FromLong

2026-09-06 18:19:55作者:郦嵘贵Just

CPython 的 C 扩展开发中,bool 类型是唯一"不需要创建和销毁"的对象类型:整个运行时只存在 Py_FalsePy_True 两个布尔对象,且自 3.12 起它们都是不可回收的 immortal(永生)对象。本篇以官方文档 Doc/c-api/bool.rst 为核心脉络,逐个讲解 PyBool_TypePyBool_CheckPy_False/Py_TruePy_RETURN_FALSE/Py_RETURN_TRUEPyBool_FromLong 等 API 的语义与版本变化,并结合 Objects/boolobject.cInclude/boolobject.h 的源码实现,说明单例是如何构造、保护以及在 C 扩展函数中正确返回的。

为什么布尔类型"不适用常规的创建与销毁"

官方文档的开篇定调了整章的核心事实:Python 中的布尔值在 C 层实现为整数(int)的子类,且全局只存在两个布尔对象 Py_FalsePy_True。因此针对其他类型常规的"创建/删除"函数(如 PyLong_FromLong 会返回一个新对象)对布尔值并不适用——你不能"新建"一个 True,只能拿到那唯一的一个。

这一设计可以直接从源码得到印证。Objects/boolobject.cPyBool_FromLong 的实现就是一行三元表达式:

PyObject *PyBool_FromLong(long ok)
{
    return ok ? Py_True : Py_False;
}

它不分配任何内存,只是按 v 的真值性在两个单例之间做选择。同样,构造器 bool_newObjects/boolobject.c)无论传入什么参数,最终也是通过 PyBool_FromLong(PyObject_IsTrue(x)) 返回这两个单例之一,这与 Python 层 bool(1) is bool(2) is True 的语义一致。

PyBool_Type 类型对象的定义也印证了"bool 是 int 的子类"这一前提:PyBool_Type 结构体中 tp_base 指向 &PyLong_TypeObjects/boolobject.c),并且类型文档字符串明确写着 "The class bool is a subclass of the class int, and cannot be subclassed"(Objects/boolobject.c)。测试用例 Lib/test/test_bool.pytest_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_TrueTrue 对象;两者都没有方法,并且是 immortal(永生对象,永不回收)。版本标注为:

  • versionchanged 3.12Py_False 是 immortal 对象。
  • versionchanged 3.12Py_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.pytest_intassertIsNot(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.hPy_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_TruePy_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_FalsePy_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_IsFalsePyBool_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 FalseObjects/boolobject.c,对应测试 Lib/test/test_bool.pytest_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_FromLongPy_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 的全部要点可归纳为:

  1. 单例模型Py_True/Py_False 是仅有的两个布尔对象,静态定义在 PyLongObject 结构体中并带独立 long 标签,自 3.12 起为 immortal;
  2. 判断与构造PyBool_Check 做精确类型匹配,PyBool_FromLong 按真值返回单例,Py_IsTrue/Py_IsFalseis True/is False 同一性判断;
  3. 返回惯例:C 扩展函数返回布尔时优先使用 Py_RETURN_TRUE/Py_RETURN_FALSE,引用计数语义由宏按 API 版本自适应;
  4. 版本边界PyBool_TypePyBool_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 中相邻的 longDoc/c-api/long.rst)与对象/引用计数(Doc/c-api/refcounting.rst)文档,它们与布尔单例的 immortal 语义直接相关。

登录后查看全文
热门项目推荐
相关项目推荐