You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python C/C++扩展中实现可装饰Python函数的自定义装饰器

C扩展实现自定义装饰器方案

核心逻辑

带参数的Python装饰器本质是三层可调用结构:

  1. 第一层接收装饰器传入的参数(你示例里的1),返回真正的装饰器函数
  2. 第二层接收被装饰的目标函数,返回包装后的函数对象
  3. 第三层被调用时,依次执行前置逻辑、原函数、后置逻辑
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 03:39:01