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

如何修改Python C API扩展模块,实现导入时调用C函数打印5的阶乘

问题

我有一段使用Python C API编写的C模块代码如下(代码无法正常运行):

#include <Python.h>

static PyObject* init_factorial(PyObject* self, PyObject* args) {
    long long n;
    if (!PyArg_ParseTuple(args, "l", &n)) {
        return NULL;
    }

    if (n < 0) {
        PyErr_SetString(PyExc_ValueError, "Factorial is not defined for negative numbers");
        return NULL;
    }

    long result = 1;
    for (long i = 1; i <= n; ++i) {
        result *= i;
    }

    return PyLong_FromLong(result);
}

static PyMethodDef init_methods[] = {
    {"factorial", init_factorial, METH_VARARGS, "Calculates the factorial of a number"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef init_module = {
    PyModuleDef_HEAD_INIT,
    "init",
    "Module for calculating factorial",
    -1,
    init_methods
};


static int init_init_module(PyObject *module) {
    // some code ...
    PyRun_SimpleString("print(factorial(5))");

    return 0;
}

PyMODINIT_FUNC PyInit_init(void) {
    PyObject *m = PyModule_Create(&init_module);
    if (m == NULL)
        return NULL;

    // Call the function to print "factorial(5)" when importing
    if (init_init_module(m) < 0) {
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

需要修改这段代码,让PyRun_SimpleString("print(factorial(5));")能在Python代码空间调用C实现的factorial函数,打印5的阶乘,且代码在导入init模块后立即执行。


核心问题分析

原代码无法运行的原因有两个:

  1. 参数解析类型不匹配:PyArg_ParseTuple用格式符"l"对应C语言的long类型,但代码中变量n是long long,会导致参数解析失败。
  2. 命名空间未绑定:PyRun_SimpleString执行的代码运行在全局解释器命名空间,而factorial是模块init的方法,未被添加到全局命名空间,会触发NameError。

修改方案及完整代码

具体修改点

  • 修正参数解析的格式符与变量类型匹配
  • 将模块对象注入到代码运行的命名空间,让Python代码能访问模块方法
  • 改用PyRun_String替代PyRun_SimpleString,手动指定运行命名空间,提升可控性
  • 可选:将阶乘结果变量改为long long,避免大数值溢出

完整修改后代码

#include <Python.h>

static PyObject* init_factorial(PyObject* self, PyObject* args) {
    // 格式符"l"对应long类型,统一变量类型避免解析错误
    long n;
    if (!PyArg_ParseTuple(args, "l", &n)) {
        return NULL;
    }

    if (n < 0) {
        PyErr_SetString(PyExc_ValueError, "Factorial is not defined for negative numbers");
        return NULL;
    }

    // 改用long long避免大数值溢出
    long long result = 1;
    for (long i = 1; i <= n; ++i) {
        result *= i;
    }

    // 对应返回long long类型的Python对象
    return PyLong_FromLongLong(result);
}

static PyMethodDef init_methods[] = {
    {"factorial", init_factorial, METH_VARARGS, "Calculates the factorial of a number"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef init_module = {
    PyModuleDef_HEAD_INIT,
    "init",
    "Module for calculating factorial",
    -1,
    init_methods
};


static int init_init_module(PyObject *module) {
    // 获取全局命名空间字典
    PyObject* global_dict = PyEval_GetGlobals();
    if (global_dict == NULL) {
        return -1;
    }

    // 将模块对象添加到全局字典,键为模块名"init"
    if (PyDict_SetItemString(global_dict, "init", module) < 0) {
        return -1;
    }

    // 通过模块名访问factorial函数
    const char* code = "print(init.factorial(5))";

    // 执行代码,指定全局/局部命名空间为当前全局字典
    PyObject* result = PyRun_String(code, Py_file_input, global_dict, global_dict);
    if (result == NULL) {
        Py_XDECREF(result);
        return -1;
    }

    Py_DECREF(result);
    return 0;
}

PyMODINIT_FUNC PyInit_init(void) {
    PyObject *m = PyModule_Create(&init_module);
    if (m == NULL)
        return NULL;

    // 调用初始化逻辑,导入模块时自动执行
    if (init_init_module(m) < 0) {
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

说明
  1. 参数解析修正后,确保Python传入的整数能正确转换为C语言变量
  2. 把模块对象注入全局命名空间后,Python代码可通过init.factorial直接调用C实现的函数
  3. PyRun_String相比PyRun_SimpleString更灵活,支持自定义代码运行的上下文环境
  4. 改用long long存储阶乘结果,能支持更大数值的计算,避免溢出问题

内容的提问来源于stack exchange,提问作者Maxim Postnikov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 06:12:37