十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python 3.15 sentinel 内置类型增强:repr 参数与可写 __module__ 全解析

Python 3.15 sentinel 内置类型增强:repr 参数与可写 __module__ 全解析 Python 3.15 sentinel 内置类型增强repr 参数与可写module全解析【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython导读本文围绕 CPython 仓库中sentinel内置类型的最新变更展开自 Python 3.15PEP 661引入sentinel类型后本次 NEWS 更新为其新增了repr构造参数并将__module__属性改为可写。文章以官方文档与源码实现为主线结合 Objects/sentinelobject.c 与 Lib/test/test_builtin.py 中的测试用例完整讲解 sentinel 的创建、属性、比较语义、类型提示、pickle 支持与 C API 用法帮助读者掌握这一标识缺失值/哨兵值的最佳实践。变更概览NEWS 条目解读本次变更记录在 Misc/NEWS.d/next/Core_and_Builtins/2026-05-10-16-43-50.gh-issue-148829.gscS14.rst原文如下sentinelobjects now support areprargument and their__module__attribute is writable.即两项能力增强sentinel()构造时支持传入repr关键字参数自定义对象的字符串表示sentinel实例的__module__属性由只读改为可写允许手动修正或重置模块归属信息。这两项能力分别由 Objects/sentinelobject.c 中的参数解析逻辑与 Objects/sentinelobject.c 中PyMemberDef的属性读写权限定义0表示可读写提供底层支撑。sentinel 类型是什么为什么需要它在 Python 编程中哨兵值sentinel value用于表示未提供缺失无默认值等特殊状态。常见做法是使用None但当None本身是合法参数值时就会产生歧义。sentinel类型正是为此而生每个 sentinel 对象都是唯一的只与自身相等适合与is运算符配合使用。从 Doc/library/functions.rst 的官方定义看其签名与语义如下sentinel(name, /, *, reprNone)name必填的位置参数必须是str默认用作对象的表示repr可选关键字参数用于自定义表示sentinel类型不支持子类化对象的浅拷贝与深拷贝都返回其自身对象为真值truthy只与自身相等。在标准库中sentinel 已被广泛采用例如 Lib/dataclasses.py 中的MISSING sentinel(MISSING)与KW_ONLY sentinel(KW_ONLY)以及 Lib/functools.py 中的_initial_missing sentinel(_initial_missing)。这也说明本次repr与__module__增强对标准库自身的可读性维护同样有实际价值。基础用法创建与表示最基本的创建方式直接传入名称字符串 MISSING sentinel(MISSING) MISSING MISSING默认情况下repr()与str()都返回name本身。从源码看这一行为由 Objects/sentinelobject.c 的sentinel_repr函数实现若内部repr字段非空则优先返回它否则返回name。核心增强一repr 参数基本用法 MISSING sentinel(MISSING, reprMISSING) MISSING MISSING str(MISSING) MISSING设置repr后repr()与str()都会返回自定义字符串这在调试输出、日志中需要更明确提示时非常有用。显式传入reprNone则与不传等价仍回退到name。参数校验repr参数必须为str或None否则抛出TypeError。这一校验逻辑位于 Objects/sentinelobject.cif (repr Py_None) { repr NULL; } else if (!PyUnicode_Check(repr)) { _PyArg_BadArgument(sentinel, argument repr, str or None, repr); return NULL; }对应的测试用例位于 Lib/test/test_builtin.pydef test_sentinel_repr(self): with_repr sentinel(WITH_REPR, reprcustom) without_repr sentinel(WITHOUT_REPR, reprNone) self.assertEqual(repr(with_repr), custom) self.assertEqual(repr(without_repr), WITHOUT_REPR) self.assertEqual(str(with_repr), custom) self.assertEqual(str(without_repr), WITHOUT_REPR) with self.assertRaisesRegex(TypeError, repr.*str or None): sentinel(BAD_REPR, repr42)底层数据结构sentinel 对象内部通过三个字段保存信息见 Objects/sentinelobject.ctypedef struct { PyObject_HEAD PyObject *name; PyObject *module; PyObject *repr; } sentinelobject;对象使用 GC 分配器创建并参与垃圾回收追踪Py_TPFLAGS_HAVE_GCrepr字段为可空引用构造时通过Py_XNewRef持有Objects/sentinelobject.c。核心增强二可写的module属性语义变化此前__module__与__name__一样只读本次变更后__module__变为可写__name__仍保持只读。成员属性表定义于 Objects/sentinelobject.cstatic PyMemberDef sentinel_members[] { {__name__, Py_T_OBJECT_EX, offsetof(sentinelobject, name), Py_READONLY}, {__module__, Py_T_OBJECT_EX, offsetof(sentinelobject, module), 0}, {NULL} };其中Py_READONLY表示只读0表示可读写。行为示例 missing sentinel(MISSING) missing.__module__ __main__ missing.__module__ changed missing.__module__ changed del missing.__module__ # 允许删除 missing.__module__ # 删除后再访问抛出 AttributeError测试用例见 Lib/test/test_builtin.py其中还验证了__name__赋值会抛出AttributeError而__module__可赋值、可删除。为何需要可写module__module__参与 pickle 的定位逻辑只有位于可导入模块全局作用域、且名称匹配的 sentinel 才能被序列化。若对象在模块间移动或需要在序列化时指定归属模块可写属性就提供了修正手段详见下文 pickle 一节。默认模块推断机制创建 sentinel 时若不显式指定模块解释器会自动推断调用方所在模块这一逻辑由 Objects/sentinelobject.c 的caller()函数完成它读取当前线程状态中的活动帧获取对应函数对象并调用PyFunction_GetModule得到模块名若当前不在函数帧内或函数无模块归属则返回None。因此在模块顶层创建时# 假设此代码位于 mypackage/constants.py MISSING sentinel(MISSING) assert MISSING.__module__ mypackage.constants比较语义与唯一性sentinel 对象为真值truthy且只与自身相等比较基于身份。官方文档明确指出其设计意图是与is运算符配合使用 missing sentinel(MISSING) other sentinel(MISSING) missing is other False missing missing True missing other False bool(missing) True即使两个 sentinel 使用相同的name它们仍是不同对象——这是区别于普通字符串常量的关键。相关断言见 Lib/test/test_builtin.py。不可子类化与类型标志sentinel是一个不可变的最终类型不可作为基类被继承类型对象本身也不允许设置属性。类型定义于 Objects/sentinelobject.cPyTypeObject PySentinel_Type { ... .tp_flags Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC, .tp_richcompare _Py_BaseObject_RichCompare, ... };测试用例验证了sentinel.__flags__中包含IMMUTABLETYPE与HAVE_GC标志且不含BASETYPELib/test/test_builtin.pywith self.assertRaises(TypeError): class SubSentinel(sentinel): pass拷贝与序列化行为拷贝返回自身__copy__与__deepcopy__均直接返回对象自身Objects/sentinelobject.c因此import copy missing sentinel(MISSING) assert copy.copy(missing) is missing assert copy.deepcopy(missing) is missingpickle 支持模块级与类级根据 Doc/library/functions.rst 的说明pickle 支持是有条件的位于模块全局作用域、且变量名与name匹配的 sentinel 可被序列化位于类作用域、名称与 sentinel 的限定名qualified name匹配的也可序列化定义在函数作用域内的 sentinel 不可序列化。序列化后身份保持不变import pickle PICKLABLE sentinel(PICKLABLE) assert pickle.loads(pickle.dumps(PICKLABLE)) is PICKLABLE class Cls: PICKLABLE sentinel(Cls.PICKLABLE) assert pickle.loads(pickle.dumps(Cls.PICKLABLE)) is Cls.PICKLABLE这一行为由 Objects/sentinelobject.c 的__reduce__实现支撑它返回name字符串pickle 将name视为模块__module__中的全局变量名进行查找还原。对应测试见 Lib/test/test_builtin.py模块级与类级 sentinel 在所有 pickle 协议下均保持身份而局部创建的 sentinel 序列化时会抛出pickle.PicklingError。类型提示中的用法sentinel 支持|按位或运算符可用于类型表达式实现默认值可能是 int 或缺失状态这样的联合类型标注MISSING sentinel(MISSING) def next_value(default: int | MISSING MISSING): ...官方文档与测试Lib/test/test_builtin.py均验证了以下行为missing | int与int | missing均生成合法的typing.Unionmissing | missing结果仍为missing自身missing | None生成(missing, NoneType)联合与任意类型操作数如list[int]、int | str组合均正常与非法操作数如1组合抛出TypeError。该能力由 Objects/sentinelobject.c 中的数字协议钩子实现static PyNumberMethods sentinel_as_number { .nb_or _Py_union_type_or, };C API 层面的支持针对嵌入与扩展场景Doc/c-api/sentinel.rst 提供了三个接口均于 Python 3.15 新增C 接口说明PySentinel_Typesentinel类型对象与 Python 层的sentinel是同一对象PySentinel_Check(o)判断o是否为 sentinel 对象或其子类由于当前不支持子类化该检查即精确匹配PySentinel_CheckExact(o)判断o是否为 sentinel 对象非子类当前与PySentinel_Check等价PySentinel_New(name, module_name, repr)创建新 sentinel三个参数均为const char *name不可为NULLmodule_name与repr可为NULL失败时返回NULL并设置异常C 层创建函数 PySentinel_New 会逐个将 C 字符串转换为PyUnicode对象并构造实例module_name传NULL时__module__被设为Nonerepr传NULL时repr()回退到name。关于 pickle 的 C 层注意事项module_name必须是可导入模块名且 sentinel 需在该模块中以与name匹配的路径可访问否则无法完成序列化还原。源码实现要点速览类型定义PySentinel_TypeObjects/sentinelobject.c同时启用IMMUTABLETYPE与HAVE_GC构造入口sentinel.__new__clinic 生成Objects/sentinelobject.cname必须是strrepr必须是str或None模块推断caller()Objects/sentinelobject.c基于当前帧的函数对象推导模块名表示逻辑sentinel_reprObjects/sentinelobject.crepr字段优先、否则回退name比较逻辑复用_Py_BaseObject_RichCompare保证基于身份的比较语义GC 支持sentinel_traverse/sentinel_clearObjects/sentinelobject.c正确管理name、module、repr三个字段的引用。兼容性与总结本次变更聚焦两点sentinel(name, *, repr...)允许自定义表示__module__变为可写。二者均为向后兼容的增量增强——原有调用方式仅传name不受影响__name__依旧只读。结合官方文档Doc/library/functions.rst、Doc/whatsnew/3.15.rst、C API 文档Doc/c-api/sentinel.rst与完整测试Lib/test/test_builtin.py可以确认 sentinel 已成为 Python 3.15 处理缺失值场景的推荐工具唯一身份、is比较、真值语义、类型表达式参与、可 pickle配合新增的repr与可写__module__无论用于标准库常量还是自定义库 API都能写出语义清晰、调试友好的哨兵值。【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表