CPython C API 函数对象实战指南:PyFunctionObject、属性访问 API 与函数生命周期 Watcher 机制
【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython
本文基于 CPython 源码仓库中的官方文档 Doc/c-api/function.rst(Function Objects 一章)编写,系统讲解 C 扩展如何创建、检查和操纵 Python 函数对象(function类型,即types.FunctionType),包括PyFunctionObject结构、PyFunction_New/PyFunction_NewWithQualName构造函数、各Get/Set属性 API、PyFunction_GET_*快速访问器、PyFunction_SetVectorcall,以及 3.12 引入的函数生命周期监视器 API(PyFunction_AddWatcher等)。读完本篇后,你能在 C 扩展中正确构造函数、读写其代码对象/默认值/闭包/注解等属性,并安全地监听函数对象的创建、销毁与关键属性变更事件。
一、函数对象是什么:PyFunctionObject 与 PyFunction_Type
文档开头指出"There are a few functions specific to Python functions"——CPython 专门为 Python 函数提供了一组 C API。核心类型有两个:
PyFunctionObject:函数对象的 C 结构体;PyFunction_Type:PyTypeObject类型的实例,代表 Python 的函数类型,在 Python 层暴露为types.FunctionType。
两者都定义在公共头文件 Include/cpython/funcobject.h 中(注意该头文件只在未启用Py_LIMITED_API时可见,即属于 CPython 扩展 API,不属于 Limited API)。
从源码结构看,PyFunctionObject的字段布局(见 funcobject.h 第 36~62 行)为:
typedef struct { PyObject_HEAD _Py_COMMON_FIELDS(func_) // 展开为 func_globals/func_builtins/ // func_name/func_qualname/func_code/ // func_defaults/func_kwdefaults/func_closure PyObject *func_doc; // __doc__ 属性,可以是任意对象 PyObject *func_dict; // __dict__ 属性,dict 或 NULL PyObject *func_weakreflist; // 弱引用列表 PyObject *func_module; // __module__ 属性,可以是任意对象 PyObject *func_annotations; // 注解,dict 或 NULL PyObject *func_annotate; // 用于填充注解字典的可调用对象 PyObject *func_typeparams; // 活跃类型变量(PEP 695)元组,或 NULL vectorcallfunc vectorcall; // vectorcall 调用入口 uint32_t func_version; // 供 Tier 1 特化器使用的版本号 } PyFunctionObject;其中_Py_COMMON_FIELDS宏展开了 8 个公共字段:func_globals(__globals__)、func_builtins(__builtins__)、func_name(__name__)、func_qualname(__qualname__)、func_code(__code__,一个 code 对象)、func_defaults(__defaults__,NULL 或元组)、func_kwdefaults(__kwdefaults__,NULL 或字典)、func_closure(__closure__,NULL 或 cell 对象元组)。头文件还声明了一条关键不变式:func_closure的长度必须等于 code 对象的自由变量数PyCode_GetNumFree(func_code)。
实现代码位于 Objects/funcobject.c。PyFunction_Type的类型对象定义见 funcobject.c 第 1275~1316 行,几个值得注意的槽位:
tp_vectorcall_offset = offsetof(PyFunctionObject, vectorcall):函数对象直接内嵌 vectorcall 函数指针,调用时走PyVectorcall_Call;Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_METHOD_DESCRIPTOR:函数是方法描述符,通过func_descr_get(见 第 1266~1273 行)在访问实例属性时自动绑定为PyMethod_New(func, obj);tp_repr为func_repr,输出形如<function f at 0x...>(使用func_qualname);tp_flags含Py_TPFLAGS_HAVE_GC,函数对象参与垃圾回收。
文档中还顺带说明了文档索引里的 MethodType 条目,但PyFunctionObject/PyFunction_Type本身只描述函数类型。
PyFunction_Check:类型检查
int PyFunction_Check(PyObject *o)文档描述:当且仅当o的类型是PyFunction_Type时返回真值;参数不得为NULL;此函数总是成功。
实现上它不是一个函数,而是头文件中的宏(见 funcobject.h 第 68 行):
#define PyFunction_Check(op) Py_IS_TYPE((op), &PyFunction_Type)即严格类型检查(不是PyObject_TypeCheck,不接受子类——事实上function类型也无从实例化出子类语义)。
二、创建函数对象:PyFunction_New 与 PyFunction_NewWithQualName
PyFunction_New
PyObject *PyFunction_New(PyObject *code, PyObject *globals)返回与 code 对象code关联的新函数对象;globals必须是函数可访问的变量所在的字典。文档明确:函数的 docstring 和名称从 code 对象获取;__module__从globals获取;参数默认值、注解和闭包被置为NULL;__qualname__被设置为与 code 对象的co_qualname相同的值。
实现上(见 funcobject.c 第 375~379 行),PyFunction_New只是一行转发:
PyObject * PyFunction_New(PyObject *code, PyObject *globals) { return PyFunction_NewWithQualName(code, globals, NULL); }PyFunction_NewWithQualName(3.3+)
PyObject *PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname)与PyFunction_New相同,但额外允许设置函数对象的__qualname__属性。qualname应为 unicode 对象或NULL;传NULL时__qualname__取 code 对象的co_qualname值。
其完整实现 PyFunction_NewWithQualName 揭示了文档描述背后的细节:
- 引用管理:对
globals和code执行_Py_INCREF_DICT/_Py_INCREF_CODE加引用,name/qualname/doc/module均持新引用; - docstring 提取:仅当
code_obj->co_flags & CO_HAS_DOCSTRING时从co_consts[0]取 doc,且必须通过PyUnicode_Check校验,否则回退为None——这解释了文档说的"docstring 从 code 对象获取"; __module__与__builtins__:通过PyDict_GetItemRef(globals, "__name__")取module(取不到则为 NULL);再用_PyDict_LoadBuiltinsFromGlobals(globals)解析出 builtins 字典;- 默认值等字段初始化为 NULL:
func_defaults = NULL、func_kwdefaults = NULL、func_closure = NULL、func_annotations = NULL,与文档"argument defaults, annotations and closure are set to NULL"一一对应; - vectorcall 与版本号:
vectorcall = _PyFunction_Vectorcall(内部默认实现,声明于 Include/internal/pycore_function.h),func_version = FUNC_VERSION_UNSET; - 延迟引用计数优化:从源码结构看(第 226~235 行),只有当 code 对象不带
CO_NESTED标志(顶层函数)或带CO_METHOD标志(类作用域内的方法)时才调用_PyObject_SetDeferredRefcount,因为嵌套函数更可能捕获变量、更需要及时析构; - 最后注册 GC(
_PyObject_GC_TRACK)并触发PyFunction_EVENT_CREATE监视器事件(见第五节)。
错误路径上,globals断言非 NULL 且必须是任意字典(PyAnyDict_Check),code断言有co_name,任一初始化步骤失败都会释放所有已持有引用后返回NULL。
Python 层的等价入口:function.new
除 C API 外,CPython 还在 Python 层提供了function类型的构造器,其签名(clinic 定义见 funcobject.c 第 1085~1102 行)为:
function.__new__(code, globals, name=None, defaults=None, closure=None, kwdefaults=None)实现 func_new_impl 会对各参数做严格校验:name必须是字符串或 None;defaults必须是元组或 None;closure必须是 cell 对象组成的元组(且长度必须等于code->co_nfreevars,否则抛ValueError);kwdefaults必须是字典或 None。校验通过后内部同样调用PyFunction_New并覆写相应字段,最后触发"function.__new__"审计事件。这与 C API 相比多了一层逐元素的闭包形态校验(C API 的PyFunction_SetClosure则只校验容器是否为元组,见下文测试中的说明)。
三、属性读取 API 一览
以下读取函数在 Doc/c-api/function.rst 中逐一列出,实现均在 Objects/funcobject.c,且全部遵循同一模式:先用PyFunction_Check校验,不是函数对象则调用PyErr_BadInternalCall()(对应 Python 层的SystemError)并返回NULL;校验通过则直接返回对应字段(返回的是借用引用,调用方不得Py_DECREF)。
| API | 返回内容 | 可能为 NULL 的情况 | 实现位置 |
|---|---|---|---|
PyFunction_GetCode(op) | 函数关联的 code 对象 | 不会(code 必非空) | L381-L389 |
PyFunction_GetGlobals(op) | __globals__字典 | 不会 | L391-L399 |
PyFunction_GetModule(op) | __module__属性 | 会(文档注明"can be NULL") | L401-L409 |
PyFunction_GetDefaults(op) | 位置参数默认值 | 会是元组或 NULL | L411-L419 |
PyFunction_GetKwDefaults(op) | 仅关键字参数默认值 | 会是字典或 NULL | L460-L468 |
PyFunction_GetClosure(op) | 闭包 | 会是 cell 元组或 NULL | L499-L507 |
PyFunction_GetAnnotations(op) | 注解 | 会是可变字典或 NULL | L585-L593 |
两点细节值得展开:
PyFunction_GetModule:文档强调返回的是__module__的借用引用、"can be NULL"、通常是一个模块名字符串但可被 Python 代码设置为任意其他对象。实现确实只是return ((PyFunctionObject *)op)->func_module;。注意这与 Python 属性f.__module__的行为略有差异:属性 getter func_get_module 在字段为 NULL 时返回None,而 C API 直接返回 NULL。PyFunction_GetAnnotations:文档说返回"mutable dictionary or NULL",但实现 func_get_annotation_dict 比"直接读字段"更复杂:若func_annotations为 NULL 但存在可调用对象func_annotate(PEP 649 的惰性注解机制),会先调用__annotate__(1)生成注解字典并缓存;若字段暂存的是元组形态,则惰性转换为字典。因此该 API 是可能执行用户代码的,与其余只读字段的 Get 函数不同。
快速访问器:PyFunction_GET_*(无类型检查)
PyObject *PyFunction_GET_CODE(PyObject *op) PyObject *PyFunction_GET_GLOBALS(PyObject *op) PyObject *PyFunction_GET_MODULE(PyObject *op) PyObject *PyFunction_GET_DEFAULTS(PyObject *op) PyObject *PyFunction_GET_KW_DEFAULTS(PyObject *op) PyObject *PyFunction_GET_CLOSURE(PyObject *op) PyObject *PyFunction_GET_ANNOTATIONS(PyObject *op)文档说明:这些函数与对应的PyFunction_Get*等价,但不做类型检查;传入非PyFunction_Type实例是未定义行为。头文件实现(见 funcobject.h 第 88~123 行)表明它们是static inline函数加同名宏:
static inline PyObject* PyFunction_GET_CODE(PyObject *func) { return _PyFunction_CAST(func)->func_code; } #define PyFunction_GET_CODE(func) PyFunction_GET_CODE(_PyObject_CAST(func))其中_PyFunction_CAST内嵌了assert(PyFunction_Check(func)),即仅在调试断言下兜底。在热路径上(例如解释器内部的_PyFunction_VerifyStateless,见 funcobject.c 第 1319~1374 行,校验函数是否"无状态"以供跨解释器共享)就直接使用这些快速访问器以避免每次调用都做类型判断。
四、属性修改 API 及其类型不变式
修改类 API 的共同点:都要求参数是Py_None或指定容器类型,失败时置异常并返回-1(PyFunction_SetKwDefaults文档措辞是"returns 0 on success, and returns -1 with an exception set on failure",与其余 Set 函数的 0/-1 约定一致)。更重要的是,从源码结构看,这些 setter 在写入字段前后都会执行同一套底层动作:
handle_func_event(PyFunction_EVENT_MODIFY_XXX, func, defaults); // 通知监视器 _PyEval_StopTheWorld(interp); // 暂停世界,保证无其他线程正在调用该函数 func_clear_version(interp, func); // 清除特化器版本号 ... 原子替换字段指针 ... _PyEval_StartTheWorld(interp); Py_XDECREF(old_xxx);StopTheWorld保证替换__defaults__/__code__/__closure__等关键字段期间不会有其他线程正基于旧值执行调用,而func_clear_version则使 Tier 1 特化器针对该函数生成的特化 CALL 指令失效(见 Objects/funcobject.c 第 251~307 行 的内部注释:func_version在 code/defaults/kwdefaults 等被修改时清零,此后该函数的调用不再被特化)。
逐个说明:
PyFunction_SetDefaults
int PyFunction_SetDefaults(PyObject *op, PyObject *defaults)defaults必须是Py_None或元组;失败时抛SystemError并返回-1。实现 PyFunction_SetDefaults 中,Py_None会被归一化为NULL(即清除默认值),元组则先Py_INCREF再替换。Python 属性__defaults__的 setter func_set_defaults 语义一致,但额外触发object.__setattr__/object.__delattr__审计事件,错误类型是TypeError而非SystemError——C API 面向"内部调用"所以报SystemError,Python 属性面向用户代码所以报TypeError。
PyFunction_SetKwDefaults
int PyFunction_SetKwDefaults(PyObject *op, PyObject *defaults)defaults必须是仅关键字参数默认值的字典或Py_None;成功返回 0,失败返回 -1 并置异常。实现见 funcobject.c 第 470~497 行,非字典时置SystemError("non-dict keyword only default args")。
PyFunction_SetClosure
int PyFunction_SetClosure(PyObject *op, PyObject *closure)closure必须是Py_None或 cell 对象元组;失败抛SystemError返回 -1。实现 PyFunction_SetClosure 只校验PyTuple_Check(closure),并不逐个校验元素是否为 cell。测试文件 Lib/test/test_capi/test_function.py 里有一条耐人寻味的注释印证了这一点:
# NOTE: this works, but goes against the docs: _testcapi.function_set_closure(function_without_closure, (1, 2))即 C API 接受非 cell 的元组元素,虽然这违背文档声明——编写扩展时应自行保证元素为 cell 对象。
PyFunction_SetAnnotations
int PyFunction_SetAnnotations(PyObject *op, PyObject *annotations)annotations必须是字典或Py_None;失败抛SystemError返回 -1。实现 PyFunction_SetAnnotations 除替换func_annotations外还会把func_annotate一并清空——即显式设置注解字典后,PEP 649 的惰性注解回调被移除。
PyFunction_SetVectorcall(3.12+)
void PyFunction_SetVectorcall(PyFunctionObject *func, vectorcallfunc vectorcall)设置函数对象的 vectorcall 字段。文档给出警告:"extensions using this API must preserve the behavior of the unaltered (default) vectorcall function!"——即自定义 vectorcall 必须保持默认_PyFunction_Vectorcall的调用语义。实现 PyFunction_SetVectorcall 与其他修改器一致:StopTheWorld+ 清版本 + 写指针 +StartTheWorld。由于PyFunction_Type声明了tp_vectorcall_offset,解释器对函数的每次调用都会读取这个字段分派,因此替换它是实现"函数包装/拦截"(例如某些计时、审计扩展)的底层手段;版本号被清除意味着 Tier 1 特化器会回退到通用 CALL 路径,这是"必须保持默认行为"警告的另一层含义。
五、函数生命周期监视器(Watcher)API(3.12+)
这是文档篇幅最大的部分,也是近年 C API 中最实用的新增能力:它允许 C 扩展像审计钩子一样监听当前解释器中函数对象的创建、销毁与关键属性修改。
事件类型 PyFunction_WatchEvent
文档列出的枚举事件:
PyFunction_EVENT_CREATEPyFunction_EVENT_DESTROYPyFunction_EVENT_MODIFY_CODEPyFunction_EVENT_MODIFY_DEFAULTSPyFunction_EVENT_MODIFY_KWDEFAULTSPyFunction_PYFUNC_EVENT_MODIFY_QUALNAME(3.15 新增)
头文件 funcobject.h 第 132~144 行 中枚举由 X-macro 展开,当前仓库实际包含的事件为CREATE、DESTROY、MODIFY_CODE、MODIFY_DEFAULTS、MODIFY_KWDEFAULTS、MODIFY_QUALNAME:
#define PY_FOREACH_FUNC_EVENT(V) \ V(CREATE) \ V(DESTROY) \ V(MODIFY_CODE) \ V(MODIFY_DEFAULTS) \ V(MODIFY_KWDEFAULTS) \ V(MODIFY_QUALNAME) typedef enum { #define PY_DEF_EVENT(EVENT) PyFunction_EVENT_##EVENT, PY_FOREACH_FUNC_EVENT(PY_DEF_EVENT) #undef PY_DEF_EVENT } PyFunction_WatchEvent;注意文档中的PyFunction_PYFUNC_EVENT_MODIFY_QUALNAME写法是文档笔误,源码里的事件名是PyFunction_EVENT_MODIFY_QUALNAME。
注册与注销
int PyFunction_AddWatcher(PyFunction_WatchCallback callback)为当前解释器注册callback作为函数监视器,返回可传给PyFunction_ClearWatcher的 ID;出错(例如没有更多可用的 watcher ID)时返回-1并置异常。
int PyFunction_ClearWatcher(int watcher_id)注销先前由PyFunction_AddWatcher返回的watcher_id。成功返回 0;出错(如该 ID 从未注册)返回 -1 并置异常。
实现细节(见 funcobject.c 第 81~114 行):监视器存放在PyInterpreterState的func_watchers数组中,用位图active_func_watchers标记活跃槽位;AddWatcher线性查找第一个空槽,返回其下标作为 ID,找不到时抛RuntimeError("no more func watcher IDs available");ClearWatcher对越界 ID 抛ValueError("invalid func watcher ID %d"),对未注册 ID 抛ValueError("no func watcher set for ID %d")。注意这是每解释器粒度的 API("for the current interpreter"),多解释器场景下需要各自注册。
回调契约 PyFunction_WatchCallback
int (*PyFunction_WatchCallback)(PyFunction_WatchEvent event, PyFunctionObject *func, PyObject *new_value);文档对回调的约束非常严格,逐条对应源码事实:
- 借用引用语义:
new_value是即将存入func的新值的借用引用;CREATE/DESTROY事件下new_value为NULL。头文件注释(funcobject.h 第 146~164 行)与文档一致。 - 只读约束:回调可以检查但不得修改func,否则"可能产生不可预测的效果,包括无限递归"。原因是事件在字段替换前触发(见下条),若在回调里再走 setter 会重入同一监视器链路。
- 事件触发时机:
CREATE事件在函数对象完全初始化之后发出(PyFunction_NewWithQualName 末尾 的handle_func_event(PyFunction_EVENT_CREATE, op, NULL));MODIFY_*事件在实际修改之前发出(各 setter 中先handle_func_event再写字段),因此回调内看到的是旧状态;DESTROY在析构路径 func_dealloc 中发出。 - 运行时优化豁免:文档说明运行时被允许"尽可能优化掉函数对象的创建",此时不会发出事件。这不会改变 Python 代码语义,但意味着监视器不应依赖 CREATE 事件计数来精确统计函数定义次数。
- DESTROY 时的"复活"语义:在销毁回调中对该函数加引用会"复活"它,推迟到稍后真正析构;届时该时刻仍活跃的监视器会再次收到 DESTROY 事件。对应实现是 func_dealloc 中的
_PyObject_ResurrectStart/_PyObject_ResurrectEnd包围handle_func_event(PyFunction_EVENT_DESTROY, op, NULL):若回调期间引用计数回升,析构直接返回,等待下次归零。 - 异常处理契约:回调若设置异常必须返回 -1,该异常会通过
PyErr_WriteUnraisable以"unraisable exception"打印;否则应返回 0。回调进入时可能已存在挂起异常——此时应带着同一异常原样返回 0,且不得调用任何可能设置异常的 API(除非先保存、清除并在返回前恢复异常状态)。实现侧 notify_func_watchers 在cb(...) < 0时调用PyErr_FormatUnraisable("Exception ignored in %s watcher callback for function %U at %p", ...),func_event_name辅助函数把枚举转成PyFunction_EVENT_XXX字符串用于报错文本。
事件分发的另一重身份:JIT/特化器失效
handle_func_event(funcobject.c 第 52~79 行)在通知监视器之后还做了一件事:对MODIFY_CODE/MODIFY_DEFAULTS/MODIFY_KWDEFAULTS/MODIFY_QUALNAME事件,在启用 Tier 2 优化(_Py_TIER2)时调用_Py_Executors_InvalidateDependency(interp, func, 1)失效依赖该函数的 JIT 代码,并累加func_modification统计。从源码结构看,监视器机制与字节码特化/JIT 的版本失效共用同一条事件通路——这也是为什么修改__code__、__defaults__、__kwdefaults__、__qualname__会触发事件而修改__name__、__doc__、__module__不会(它们的 setter 不经过handle_func_event)。
六、测试如何验证这套 API
CPython 标准库测试 Lib/test/test_capi/test_function.py 通过辅助扩展_testcapi(C 封装见 Modules/_testcapi/function.c)对上述 API 做了端到端验证,值得作为使用范式参考:
- 每个
PyFunction_Get*都用"正常函数"与"非函数对象(如None/1)"两类输入断言:前者结果与f.__code__、f.__globals__、f.__defaults__等 Python 属性一致;后者期望SystemError(对应PyErr_BadInternalCall())。例如:
code = _testcapi.function_get_code(some) self.assertEqual(code, some.__code__) with self.assertRaises(SystemError): _testcapi.function_get_code(None) # not a functionPyFunction_SetDefaults测试覆盖了错误输入(非元组、非函数对象均抛SystemError且原值不变)、空元组、元组子类("tuple subclasses must work",因实现只要求PyTuple_Check)以及None归一化为 NULL 后属性读取返回None;- 闭包测试验证了"无闭包函数返回 NULL、有闭包时长度等于
co_freevars、元素为 cell"的不变式(测试中用types.CellType手工构造 cell 元组调用PyFunction_SetClosure); - 文件末尾注释标明
PyFunction_AddWatcher/PyFunction_ClearWatcher由test_capi.test_watchers专门测试(# PyFunction_AddWatcher() and PyFunction_ClearWatcher() are tested by test_capi.test_watchers.)。
这些测试同时印证了本文各节给出的语义:Get 系列返回借用引用(封装里Py_NewRef一次再返回)、Set 系列的 0/-1 与异常类型、以及 C API 与 Python 属性之间的细微差别。
七、速查与使用注意
把 Doc/c-api/function.rst 的 API 汇总为速查表("版本"列为文档标注的 versionadded,当前仓库版本号为 3.16.0a0,见 Include/patchlevel.h,故全部 API 均可用):
| API | 说明 | 错误语义 | 版本 |
|---|---|---|---|
PyFunction_Check(o) | 类型检查,非 NULL 参数 | 总是成功 | - |
PyFunction_New(code, globals) | 创建函数,__qualname__取co_qualname | 失败返回 NULL | - |
PyFunction_NewWithQualName(code, globals, qualname) | 同上且可指定__qualname__(unicode 或 NULL) | 失败返回 NULL | 3.3 |
PyFunction_GetCode/GetGlobals/GetModule/GetDefaults/GetKwDefaults/GetClosure/GetAnnotations | 读取对应属性,返回借用引用 | 非函数输入置SystemError返回 NULL | - |
PyFunction_SetDefaults(op, defaults) | Py_None或元组 | SystemError,返回 -1 | - |
PyFunction_SetKwDefaults(op, defaults) | Py_None或字典 | 置异常,返回 -1 | - |
PyFunction_SetClosure(op, closure) | Py_None或 cell 元组 | SystemError,返回 -1 | - |
PyFunction_SetAnnotations(op, annotations) | Py_None或字典(同时清除func_annotate) | SystemError,返回 -1 | - |
PyFunction_SetVectorcall(func, vectorcall) | 替换 vectorcall 入口,须保持默认语义 | 无(void) | 3.12 |
PyFunction_GET_* | 无类型检查的快速访问器,误用是 UB | - | - |
PyFunction_AddWatcher(cb) | 注册监视器,返回 ID | 无可用 ID 时置异常返回 -1 | 3.12 |
PyFunction_ClearWatcher(id) | 注销监视器 | ID 非法/未注册时置异常返回 -1 | 3.12 |
几条实践性提醒:
- 引用计数纪律:所有
Get返回借用引用,跨 API 边界传递前需Py_NewRef;Set系列对传入对象自行加引用,调用方保留自己原有的引用不变; - NULL 语义分层:
__module__、__defaults__、__kwdefaults__、__closure__、__annotations__在 C 层都可能是 NULL,而 Python 属性视角下通常呈现为None(如func_get_module把 NULL 映射为None),跨层比对时留意这一差异; - 修改类 API 会 StopTheWorld 并使特化/JIT 依赖失效,因此它们不是"廉价字段写入",高频路径上应避免反复重写
__code__/__defaults__等; - watcher 回调是无锁重入环境:不得修改func、不得随意调用可能设置异常的 API(除非妥善保存/恢复异常状态),且要容忍"函数创建被优化掉导致事件缺失"与"DESTROY 回调中复活对象引发二次 DESTROY"两种边界情况。
八、延伸阅读路径
- 官方文档源文件:Doc/c-api/function.rst(本文的骨架,含全部
.. c:function::定义与 versionadded 标注); - 类型与结构定义:Include/cpython/funcobject.h(
PyFunctionObject、PyFunction_Check宏、PY_FOREACH_FUNC_EVENT、回调签名),以及同文件中的PyClassMethod_Type/PyStaticMethod_Type(classmethod/staticmethod 类型定义与函数类型同处一文件); - 核心实现:Objects/funcobject.c(
PyFunction_NewWithQualName、各 Get/Set、function.__new__、PyFunction_Type类型表、PyFunction_Type的 dealloc/repr/traverse、_PyFunction_VerifyStateless); - 测试:Lib/test/test_capi/test_function.py 与 C 封装 Modules/_testcapi/function.c。
【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考