如何在Python C/C++扩展中实现可装饰Python函数的自定义装饰器
C扩展实现自定义装饰器方案
核心逻辑
带参数的Python装饰器本质是三层可调用结构:
- 第一层接收装饰器传入的参数(你示例里的
1),返回真正的装饰器函数 - 第二层接收被装饰的目标函数,返回包装后的函数对象
- 第三层被调用时,依次执行前置逻辑、原函数、后置逻辑
C扩展里只要对应实现这三层的可调用逻辑即可,无需特殊API,用标准CPython的函数定义、对象状态存储接口就能完成。
完整C扩展代码示例
#include <Python.h> // 存储wrapper需要的上下文:装饰器参数、原函数 typedef struct { PyObject_HEAD int decorator_arg; PyObject *original_func; } WrapperObject; // wrapper函数的调用逻辑,也就是实际执行的逻辑 static PyObject* Wrapper_call(WrapperObject *self, PyObject *args, PyObject *kwargs) { // 前置逻辑:这里写你要在C里实现的前置代码 printf("C层前置逻辑,拿到的装饰器参数是:%d\n", self->decorator_arg); // 调用原函数 PyObject *result = PyObject_Call(self->original_func, args, kwargs); if (result == NULL) { return NULL; // 原函数抛异常直接透传 } // 后置逻辑:这里写你要在C里实现的后置代码 printf("C层后置逻辑执行完成\n"); return result; } // Wrapper对象的析构函数,处理引用计数 static void Wrapper_dealloc(WrapperObject *self) { Py_XDECREF(self->original_func); Py_TYPE(self)->tp_free((PyObject*)self); } // 定义Wrapper类型 static PyTypeObject WrapperType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "my_c_extension.Wrapper", .tp_basicsize = sizeof(WrapperObject), .tp_dealloc = (destructor)Wrapper_dealloc, .tp_call = (ternaryfunc)Wrapper_call, .tp_flags = Py_TPFLAGS_DEFAULT, }; // 第二层:接收被装饰的原函数,返回wrapper对象 static PyObject* real_decorator(PyObject *self, PyObject *args) { int decorator_arg = PyLong_AsLong(self); // 拿到第一层存的装饰器参数 PyObject *original_func; if (!PyArg_ParseTuple(args, "O", &original_func)) { return NULL; } if (!PyCallable_Check(original_func)) { PyErr_SetString(PyExc_TypeError, "参数必须是可调用对象"); return NULL; } // 创建wrapper对象,存参数和原函数 WrapperObject *wrapper = PyObject_New(WrapperObject, &WrapperType); if (wrapper == NULL) { return NULL; } wrapper->decorator_arg = decorator_arg; Py_INCREF(original_func); wrapper->original_func = original_func; return (PyObject*)wrapper; } // 第一层:接收装饰器传入的参数,返回真正的装饰器 static PyObject* my_decorator(PyObject *self, PyObject *args) { int arg; if (!PyArg_ParseTuple(args, "i", &arg)) { return NULL; } // 把参数绑定到real_decorator上,返回这个绑定后的可调用对象 return PyCFunction_New(&(PyMethodDef){ .ml_name = "real_decorator", .ml_meth = real_decorator, .ml_flags = METH_VARARGS, }, PyLong_FromLong(arg)); } // 扩展方法表 static PyMethodDef MyExtensionMethods[] = { {"my_decorator", my_decorator, METH_VARARGS, "自定义带参数的装饰器"}, {NULL, NULL, 0, NULL} // 哨兵 }; // 扩展模块定义 static struct PyModuleDef my_c_extension_module = { PyModuleDef_HEAD_INIT, .m_name = "my_c_extension", .m_size = -1, .m_methods = MyExtensionMethods, }; // 模块初始化函数 PyMODINIT_FUNC PyInit_my_c_extension(void) { PyObject *module; // 初始化Wrapper类型 if (PyType_Ready(&WrapperType) < 0) { return NULL; } module = PyModule_Create(&my_c_extension_module); if (module == NULL) { return NULL; } return module; }
编译测试方法
编写setup.py编译配置
from setuptools import setup, Extension setup( name="my_c_extension", version="1.0", ext_modules=[Extension("my_c_extension", sources=["my_c_extension.c"])] )
编译安装
执行命令:python setup.py install
测试代码
import my_c_extension as m @m.my_decorator(1) def func(): print("Python层原函数执行") func()
运行后的输出为:
C层前置逻辑,拿到的装饰器参数是:1 Python层原函数执行 C层后置逻辑执行
关键注意点
- 引用计数必须正确管理:存储的原函数、装饰器参数需要手动
INCREF,对象销毁时手动DECREF,避免内存泄漏或者访问野指针 - 原函数调用时如果返回NULL说明有异常抛出,要直接返回NULL把异常透传到Python层,不要吞异常
- 如果需要支持带参数的原函数,直接在
Wrapper_call里把args和kwargs透传给PyObject_Call即可,不需要额外处理参数适配 - 你可以根据需求修改前置、后置逻辑的C实现,替换示例中的printf代码即可
内容的提问来源于stack exchange,提问作者jasbury
相关产品推荐
相关产品推荐

