news 2026/9/7 4:30:54

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CPython C API 函数对象实战指南:PyFunctionObject、属性访问 API 与函数生命周期 Watcher 机制

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_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.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_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)位置参数默认值会是元组或 NULLL411-L419
PyFunction_GetKwDefaults(op)仅关键字参数默认值会是字典或 NULLL460-L468
PyFunction_GetClosure(op)闭包会是 cell 元组或 NULLL499-L507
PyFunction_GetAnnotations(op)注解会是可变字典或 NULLL585-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_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_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)失败返回 NULL3.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_annotateSystemError,返回 -1-
PyFunction_SetVectorcall(func, vectorcall)替换 vectorcall 入口,须保持默认语义无(void)3.12
PyFunction_GET_*无类型检查的快速访问器,误用是 UB--
PyFunction_AddWatcher(cb)注册监视器,返回 ID无可用 ID 时置异常返回 -13.12
PyFunction_ClearWatcher(id)注销监视器ID 非法/未注册时置异常返回 -13.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.h(PyFunctionObjectPyFunction_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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 4:28:56

Unity PSD转UI自动化:ETYIUI实现预制体生成与代码绑定全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 4:28:50

技术博客写作指南:开源项目与本地部署的工程实践

抱歉&#xff0c;这个输入内容不适合改写成 CSDN 技术博客。任务标题看起来是一个社交网络上的 Meme 梗合集&#xff0c;没有开源项目信息、功能说明、部署方式或可验证的技术过程&#xff0c;不符合 CSDN 的领域定位&#xff0c;也不适合作为技术教程发布。如果你手头有本地部…

作者头像 李华
网站建设 2026/9/7 4:28:41

婚介客户管理系统与相亲小程序开发实战

你提供的主题是一则婚介广告文案&#xff0c;与 CSDN 技术博客的定位完全不匹配。CSDN 是面向开发者、软件工程师和科技从业者的技术社区&#xff0c;发布的核心内容是编程语言、框架、架构、人工智能、运维、数据库、工具链和工程实践等方向&#xff0c;帮助读者解决实际开发问…

作者头像 李华
网站建设 2026/9/7 4:27:11

MATLAB/Simulink单相交流调压电路仿真:晶闸管触发与波形分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 4:23:28

基于openEuler鲲鹏平台的Agent Memory记忆管理系统实现指南

Agent Memory 记忆管理系统&#xff0c;可以理解为给 AI Agent 增加一套长期记忆仓库&#xff1a;对话结束后&#xff0c;关键信息仍然保留&#xff1b;下次交互&#xff0c;Agent 能直接读取旧记忆并继续工作。在 2026 中国国际大学生创新大赛的 openEuler 方向赛题里&#xf…

作者头像 李华