首页
/ CPython C API 函数对象实战指南:PyFunctionObject、属性访问 API 与函数生命周期 Watcher 机制

CPython C API 函数对象实战指南:PyFunctionObject、属性访问 API 与函数生命周期 Watcher 机制

2026-09-06 11:51:30作者:吴年前Myrtle

本文基于 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_TypePyTypeObject 类型的实例,代表 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.cPyFunction_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_reprfunc_repr,输出形如 <function f at 0x...>(使用 func_qualname);
  • tp_flagsPy_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 揭示了文档描述背后的细节:

  1. 引用管理:对 globalscode 执行 _Py_INCREF_DICT/_Py_INCREF_CODE 加引用,name/qualname/doc/module 均持新引用;
  2. docstring 提取:仅当 code_obj->co_flags & CO_HAS_DOCSTRING 时从 co_consts[0] 取 doc,且必须通过 PyUnicode_Check 校验,否则回退为 None——这解释了文档说的"docstring 从 code 对象获取";
  3. __module____builtins__:通过 PyDict_GetItemRef(globals, "__name__")module(取不到则为 NULL);再用 _PyDict_LoadBuiltinsFromGlobals(globals) 解析出 builtins 字典;
  4. 默认值等字段初始化为 NULLfunc_defaults = NULLfunc_kwdefaults = NULLfunc_closure = NULLfunc_annotations = NULL,与文档"argument defaults, annotations and closure are set to NULL"一一对应;
  5. vectorcall 与版本号vectorcall = _PyFunction_Vectorcall(内部默认实现,声明于 Include/internal/pycore_function.h),func_version = FUNC_VERSION_UNSET
  6. 延迟引用计数优化:从源码结构看(第 226~235 行),只有当 code 对象不带 CO_NESTED 标志(顶层函数)或带 CO_METHOD 标志(类作用域内的方法)时才调用 _PyObject_SetDeferredRefcount,因为嵌套函数更可能捕获变量、更需要及时析构;
  7. 最后注册 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 或指定容器类型,失败时置异常并返回 -1PyFunction_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_CREATE
  • PyFunction_EVENT_DESTROY
  • PyFunction_EVENT_MODIFY_CODE
  • PyFunction_EVENT_MODIFY_DEFAULTS
  • PyFunction_EVENT_MODIFY_KWDEFAULTS
  • PyFunction_PYFUNC_EVENT_MODIFY_QUALNAME(3.15 新增)

头文件 funcobject.h 第 132~144 行 中枚举由 X-macro 展开,当前仓库实际包含的事件为 CREATEDESTROYMODIFY_CODEMODIFY_DEFAULTSMODIFY_KWDEFAULTSMODIFY_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 行):监视器存放在 PyInterpreterStatefunc_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);

文档对回调的约束非常严格,逐条对应源码事实:

  1. 借用引用语义new_value 是即将存入 func 的新值的借用引用;CREATE/DESTROY 事件下 new_valueNULL。头文件注释(funcobject.h 第 146~164 行)与文档一致。
  2. 只读约束:回调可以检查但不得修改 func,否则"可能产生不可预测的效果,包括无限递归"。原因是事件在字段替换前触发(见下条),若在回调里再走 setter 会重入同一监视器链路。
  3. 事件触发时机CREATE 事件在函数对象完全初始化之后发出(PyFunction_NewWithQualName 末尾handle_func_event(PyFunction_EVENT_CREATE, op, NULL));MODIFY_* 事件在实际修改之前发出(各 setter 中先 handle_func_event 再写字段),因此回调内看到的是旧状态;DESTROY 在析构路径 func_dealloc 中发出。
  4. 运行时优化豁免:文档说明运行时被允许"尽可能优化掉函数对象的创建",此时不会发出事件。这不会改变 Python 代码语义,但意味着监视器不应依赖 CREATE 事件计数来精确统计函数定义次数。
  5. DESTROY 时的"复活"语义:在销毁回调中对该函数加引用会"复活"它,推迟到稍后真正析构;届时该时刻仍活跃的监视器会再次收到 DESTROY 事件。对应实现是 func_dealloc 中的 _PyObject_ResurrectStart/_PyObject_ResurrectEnd 包围 handle_func_event(PyFunction_EVENT_DESTROY, op, NULL):若回调期间引用计数回升,析构直接返回,等待下次归零。
  6. 异常处理契约:回调若设置异常必须返回 -1,该异常会通过 PyErr_WriteUnraisable 以"unraisable exception"打印;否则应返回 0。回调进入时可能已存在挂起异常——此时应带着同一异常原样返回 0,且不得调用任何可能设置异常的 API(除非先保存、清除并在返回前恢复异常状态)。实现侧 notify_func_watcherscb(...) < 0 时调用 PyErr_FormatUnraisable("Exception ignored in %s watcher callback for function %U at %p", ...)func_event_name 辅助函数把枚举转成 PyFunction_EVENT_XXX 字符串用于报错文本。

事件分发的另一重身份:JIT/特化器失效

handle_func_eventfuncobject.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 function
  • PyFunction_SetDefaults 测试覆盖了错误输入(非元组、非函数对象均抛 SystemError 且原值不变)、空元组、元组子类("tuple subclasses must work",因实现只要求 PyTuple_Check)以及 None 归一化为 NULL 后属性读取返回 None
  • 闭包测试验证了"无闭包函数返回 NULL、有闭包时长度等于 co_freevars、元素为 cell"的不变式(测试中用 types.CellType 手工构造 cell 元组调用 PyFunction_SetClosure);
  • 文件末尾注释标明 PyFunction_AddWatcher/PyFunction_ClearWatchertest_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

几条实践性提醒:

  1. 引用计数纪律:所有 Get 返回借用引用,跨 API 边界传递前需 Py_NewRefSet 系列对传入对象自行加引用,调用方保留自己原有的引用不变;
  2. NULL 语义分层__module____defaults____kwdefaults____closure____annotations__ 在 C 层都可能是 NULL,而 Python 属性视角下通常呈现为 None(如 func_get_module 把 NULL 映射为 None),跨层比对时留意这一差异;
  3. 修改类 API 会 StopTheWorld 并使特化/JIT 依赖失效,因此它们不是"廉价字段写入",高频路径上应避免反复重写 __code__/__defaults__ 等;
  4. watcher 回调是无锁重入环境:不得修改 func、不得随意调用可能设置异常的 API(除非妥善保存/恢复异常状态),且要容忍"函数创建被优化掉导致事件缺失"与"DESTROY 回调中复活对象引发二次 DESTROY"两种边界情况。

八、延伸阅读路径

  • 官方文档源文件:Doc/c-api/function.rst(本文的骨架,含全部 .. c:function:: 定义与 versionadded 标注);
  • 类型与结构定义:Include/cpython/funcobject.hPyFunctionObjectPyFunction_Check 宏、PY_FOREACH_FUNC_EVENT、回调签名),以及同文件中的 PyClassMethod_Type/PyStaticMethod_Type(classmethod/staticmethod 类型定义与函数类型同处一文件);
  • 核心实现:Objects/funcobject.cPyFunction_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
登录后查看全文
热门项目推荐
相关项目推荐