CPython C API 函数对象实战指南:PyFunctionObject、属性访问 API 与函数生命周期 Watcher 机制
本文基于 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 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_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。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0625
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00