资讯详情

资讯详情

CPython 3.15 C API 新增 PySentinel_CheckExact:为 sentinel 内置类型补充精确类型检测

CPython 3.15 C API 新增 PySentinel_CheckExact为 sentinel 内置类型补充精确类型检测【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython本文聚焦 CPython 3.15 的一条 C API 变更记录为内置的sentinel类型新增PySentinel_CheckExact宏与既有的PySentinel_Check形成“子类型/精确类型”双检测的完整组合。读完后你能够理解sentinel类型在 C API 层的完整接口面、Check 与 CheckExact 的语义差异及其设计动机并知道如何在 C 扩展中正确地创建与判定 sentinel 对象。1. 变更记录这条 NEWS 说了什么本次变更对应的变更说明位于 Misc/NEWS.d/next/C_API/2026-05-12-16-47-21.gh-issue-149725.HZLBTZ.rst原文仅两行AddPySentinel_CheckExactfor exactsentineltype tests to accompany the existingPySentinel_Check.即新增PySentinel_CheckExact用于对sentinel类型做精确不匹配子类的类型判定以配套已存在的PySentinel_Check允许匹配子类。虽然条目简短但它补全了 CPython C API 中长期遵循的一个惯例每个“可实例化的核心类型”通常同时提供_Check与_CheckExact两个判定接口例如PyList_Check与PyList_CheckExact本条变更让sentinel补齐了这一惯例。2. 背景3.15 引入的 sentinel 内置类型sentinel是 Python 3.15 通过 PEP 661 引入的新内置类型见 Doc/whatsnew/3.15.rst 的 “Add sentinel built-in type” 一节A newsentineltype is added to thebuiltinsmodule for creating unique sentinel values with a concise representation. Sentinel objects preserve identity when copied, support use in type expressions with the|operator, and can be pickled when they are importable by module and name.其典型用途是替代过去用object()、私有类实例或_MISSING object()手写“唯一占位值”的惯用法。从实现 Objects/sentinelobject.c 可以看到几个关键语义每个 sentinel 对象保存三个字符串字段name、module、repr对应结构体sentinelobjectObjects/sentinelobject.c。__copy__与__deepcopy__均返回自身引用sentinel_copy/sentinel_deepcopyObjects/sentinelobject.c保证复制后身份不变。nb_or槽位接入了_Py_union_type_orObjects/sentinelobject.c因此 sentinel 可以参与X | sentinel这类类型表达式。__reduce__只返回nameObjects/sentinelobject.c这是其可 pickle 机制的基础。类型标志包含Py_TPFLAGS_IMMUTABLETYPEObjects/sentinelobject.c即当前不允许子类化——这一点正是理解 Check 与 CheckExact 差异的关键。3. C API 接口面PySentinel_Type 家族sentinel的 C API 文档见 Doc/c-api/sentinel.rst共四个条目均标注versionadded:: 3.15接口形式语义PySentinel_TypePyTypeObject数据sentinel类型的类型对象与 Python 层sentinel是同一对象PySentinel_Check(o)函数宏o是sentinel或其子类时返回真因当前禁止子类化该检查等价于精确检查。该函数“always succeeds”不会失败或设置异常PySentinel_CheckExact(o)函数宏o严格是sentinel而非子类时返回真。同样 always succeedsPySentinel_New(name, module_name, repr)函数创建一个新 sentinel 对象name不能为NULLmodule_name为NULL时__module__为Nonerepr为NULL时repr()回退为__name__失败时返回NULL并设置异常其中PySentinel_New对 pickle 有一个重要约束若要可被 picklemodule_name必须是可导入模块名且该 sentinel 能经由与name匹配的路径从该模块访问——因为 pickle 会把name当作module_name中的全局变量名对应object.__reduce__机制。4. 源码证据CheckExact 的实现与“别名”设计本次新增的宏定义位于公共CPython 内部风格头文件 Include/cpython/sentinelobject.hPyAPI_DATA(PyTypeObject) PySentinel_Type; #define PySentinel_CheckExact(op) Py_IS_TYPE((op), PySentinel_Type) /* Alias as long as subclasses are not allowed. */ #define PySentinel_Check(op) PySentinel_CheckExact(op)两个细节值得注意PySentinel_CheckExact是一次精确的类型指针比较Py_IS_TYPE((op), PySentinel_Type)直接比较对象的类型对象与PySentinel_Type不遍历tp_bases因此 O(1) 且语义严格。当前PySentinel_Check就是PySentinel_CheckExact的别名头文件中的注释 “Alias as long as subclasses are not allowed.” 与 Doc/c-api/sentinel.rst 中 “Future Python versions may choose to allow subtyping.” 的说明相互印证。也就是说这条 NEWS 变更的真正价值在于为未来的子类化预留了正确的语义分界一旦 3.15 之后的版本允许sentinel的子类PySentinel_Check将放宽为isinstance语义而PySentinel_CheckExact保持不变。C 扩展作者若今天就显式使用CheckExact将来无需修改代码即可维持“只认 sentinel 本尊”的行为——这正是 CPython 一贯的 API 前瞻性约定与PyList_CheckExact、PyDict_CheckExact等的设计动机一致。另外注意整个头文件被#ifndef Py_LIMITED_API包裹Include/cpython/sentinelobject.h即这套接口不进入 Limited API使用它们构建的扩展需要链接完整的 CPython 私有实现不能以 limited-API 模式编译。5. 配套函数 PySentinel_New 的实现Objects/sentinelobject.c 中的PySentinel_New实现清晰地呈现了参数规则PyObject * PySentinel_New(const char *name, const char *module_name, const char *repr) { PyObject *name_obj PyUnicode_FromString(name); if (name_obj NULL) { return NULL; } PyObject *repr_obj NULL; if (repr ! NULL) { repr_obj PyUnicode_FromString(repr); ... } PyObject *module_obj module_name NULL ? Py_None : PyUnicode_FromString(module_name); ... PyObject *sentinel sentinel_new_with_module( PySentinel_Type, name_obj, module_obj, repr_obj); ... return sentinel; }name为NULL时PyUnicode_FromString失败返回NULL并设置异常与文档“namemust not be NULL”一致module_name为NULL时显式构造Py_None即__module__为None三个字符串对象都经Py_NewRef/Py_XNewRef移交所有权存入对象见sentinel_new_with_moduleObjects/sentinelobject.c对象通过PyObject_GC_New分配并纳入 GC 追踪。Python 层的sentinel.__new__sentinel_new_impl则在此基础上自动把module设为调用方所在模块caller()从当前帧的函数对象取__module__Objects/sentinelobject.c而 C API 版本把模块名的决定权交给 C 调用者便于 C 扩展构造可 pickle 的 sentinel。6. 测试验证从 _testcapi 到标准测试该接口通过测试扩展暴露并验证。Modules/_testcapi/object.c 提供了三个封装static PyObject * pysentinel_new(PyObject *self, PyObject *args) { ... if (!PyArg_ParseTuple(args, s|ss, name, module_name, repr)) { return NULL; } return PySentinel_New(name, module_name, repr); } static PyObject * pysentinel_check(PyObject *self, PyObject *obj) { return PyBool_FromLong(PySentinel_Check(obj)); } static PyObject * pysentinel_checkexact(PyObject *self, PyObject *obj) { return PyBool_FromLong(PySentinel_CheckExact(obj)); }对应的标准测试 Lib/test/test_capi/test_object.py 覆盖了正反两种判定def test_pysentinel_new(self): marker _testcapi.pysentinel_new(CAPI_SENTINEL, __name__) self.assertIs(type(marker), sentinel) self.assertTrue(_testcapi.pysentinel_check(marker)) self.assertFalse(_testcapi.pysentinel_check(object())) self.assertTrue(_testcapi.pysentinel_checkexact(marker)) self.assertFalse(_testcapi.pysentinel_checkexact(object()))即对 sentinel 实例两个判定都为真对普通object()都为假。该测试同时验证了PySentinel_New的三种参数组合仅 name、带 module、带 repr。7. 在 C 扩展中的典型用法综合上述接口一个典型的 C 扩展用法基于 Doc/c-api/sentinel.rst 的接口契约如下#include Python.h #include cpython/sentinelobject.h /* 在模块初始化时创建一个可 pickle 的 sentinel */ static PyObject *missing_sentinel; static int module_init(void) { /* name 必须非 NULLmodule 必须可导入且能找到同名全局变量 */ missing_sentinel PySentinel_New(missing, mylib, mylib.missing); return missing_sentinel NULL ? -1 : 0; } /* 精确判定只认 sentinel 本尊 */ static PyObject * mylib_get_default(PyObject *self, PyObject *arg) { if (arg NULL || PySentinel_CheckExact(arg)) { Py_INCREF(missing_sentinel); return missing_sentinel; } Py_INCREF(arg); return arg; }使用注意判定函数“always succeeds”不会设置异常失败时非 sentinel仅返回 0无需检查PyErr_Occurred()创建函数可能失败必须检查NULL返回并处理异常若扩展以Py_LIMITED_API编译Include/cpython/sentinelobject.h中的这些符号不可见这是当前版本的使用前提限制。8. 小结这条两行的 NEWS 条目gh-issue-149725本身很小但它是 CPython 3.15sentinel内置类型PEP 661C API 接口面拼图的最后一块PySentinel_TypeInclude/cpython/sentinelobject.h提供类型对象PySentinel_Check/PySentinel_CheckExactInclude/cpython/sentinelobject.h提供 O(1) 的类型判定且通过“Check 目前是 CheckExact 别名 文档保留未来子类化空间”的方式为潜在的类型演化预置了语义分界PySentinel_NewObjects/sentinelobject.c提供参数受控的对象创建入口测试链_testcapiModules/_testcapi/object.c → Lib/test/test_capi/test_object.py保证上述行为可回归验证。对 C 扩展作者而言今天显式选用PySentinel_CheckExact是成本最低的“面向未来”写法无论 CPython 将来是否放开sentinel的子类化该宏的语义都不会漂移。【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →