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

在C++中获取Python函数的参数名称

获取Python函数参数名的C++实现方案

我之前刚好碰到过类似的需求——在C++里调用Python函数时需要按名传参,而且要兼容.pyc文件。不用反编译字节码,完全通过Python C API就能搞定,给你两个实用的方案:

方案一:借助Python的inspect模块(简洁易维护)

Python标准库的inspect模块本来就提供了获取函数签名的能力,我们可以在C++里直接调用它的API来拿到参数名,不用自己造轮子。步骤大概是这样:

  1. 先拿到你要调用的Python函数对象(不管是从.py还是.pyc加载的模块里获取,方法都是一样的)
  2. 导入inspect模块,调用getfullargspec或者signature方法
  3. 解析返回的结果,提取参数名称列表

下面是一段示例代码:

#include <Python.h>
#include <vector>
#include <string>

std::vector<std::string> get_python_function_args(PyObject* func) {
    std::vector<std::string> args;

    // 检查输入是否是函数对象
    if (!PyFunction_Check(func)) {
        PyErr_SetString(PyExc_TypeError, "Expected a function object");
        return args;
    }

    // 导入inspect模块
    PyObject* inspect_module = PyImport_ImportModule("inspect");
    if (!inspect_module) {
        PyErr_Print();
        return args;
    }

    // 获取getfullargspec函数
    PyObject* getfullargspec_func = PyObject_GetAttrString(inspect_module, "getfullargspec");
    Py_DECREF(inspect_module);
    if (!getfullargspec_func || !PyCallable_Check(getfullargspec_func)) {
        PyErr_Print();
        Py_XDECREF(getfullargspec_func);
        return args;
    }

    // 调用getfullargspec(func)
    PyObject* argspec = PyObject_CallFunctionObjArgs(getfullargspec_func, func, NULL);
    Py_DECREF(getfullargspec_func);
    if (!argspec) {
        PyErr_Print();
        return args;
    }

    // 从argspec中提取args列表(第一个元素是参数名列表)
    PyObject* args_list = PyTuple_GetItem(argspec, 0);
    if (!args_list || !PyList_Check(args_list)) {
        PyErr_Print();
        Py_DECREF(argspec);
        return args;
    }

    // 遍历列表,把每个参数名转成C++字符串
    Py_ssize_t arg_count = PyList_Size(args_list);
    for (Py_ssize_t i = 0; i < arg_count; ++i) {
        PyObject* arg_name_obj = PyList_GetItem(args_list, i);
        if (arg_name_obj && PyUnicode_Check(arg_name_obj)) {
            const char* arg_name = PyUnicode_AsUTF8(arg_name_obj);
            if (arg_name) {
                args.push_back(arg_name);
            }
        }
    }

    Py_DECREF(argspec);
    return args;
}

这个方法的好处是,getfullargspec会帮你处理各种复杂情况,比如默认参数、可变参数(*args/**kwargs)、关键字-only参数,返回的结果里还包含参数的默认值等信息,如果你需要的话也能一起拿到。

方案二:直接操作函数的__code__属性(更底层高效)

如果你不想依赖inspect模块,可以直接访问Python函数对象的__code__属性,里面存储了函数的字节码元数据,包括参数名。

每个Python函数都有一个__code__对象,其中:

  • co_argcount:普通位置参数的数量
  • co_varnames:一个元组,包含函数内所有局部变量的名称,前co_argcount个就是参数名(如果有可变参数或关键字-only参数,需要结合co_kwonlyargcount等属性处理)

示例代码如下:

#include <Python.h>
#include <vector>
#include <string>

std::vector<std::string> get_python_function_args_direct(PyObject* func) {
    std::vector<std::string> args;

    if (!PyFunction_Check(func)) {
        PyErr_SetString(PyExc_TypeError, "Expected a function object");
        return args;
    }

    // 获取函数的__code__属性
    PyObject* code_obj = PyObject_GetAttrString(func, "__code__");
    if (!code_obj) {
        PyErr_Print();
        return args;
    }

    // 获取普通位置参数数量
    PyObject* arg_count_obj = PyObject_GetAttrString(code_obj, "co_argcount");
    if (!arg_count_obj || !PyLong_Check(arg_count_obj)) {
        PyErr_Print();
        Py_DECREF(code_obj);
        return args;
    }
    Py_ssize_t arg_count = PyLong_AsSsize_t(arg_count_obj);
    Py_DECREF(arg_count_obj);

    // 获取变量名称元组
    PyObject* varnames_obj = PyObject_GetAttrString(code_obj, "co_varnames");
    if (!varnames_obj || !PyTuple_Check(varnames_obj)) {
        PyErr_Print();
        Py_DECREF(code_obj);
        return args;
    }
    Py_DECREF(code_obj);

    // 提取前arg_count个元素作为普通参数名
    for (Py_ssize_t i = 0; i < arg_count; ++i) {
        PyObject* arg_name_obj = PyTuple_GetItem(varnames_obj, i);
        if (arg_name_obj && PyUnicode_Check(arg_name_obj)) {
            const char* arg_name = PyUnicode_AsUTF8(arg_name_obj);
            if (arg_name) {
                args.push_back(arg_name);
            }
        }
    }

    // 处理Python 3+的关键字-only参数
    PyObject* kwonly_count_obj = PyObject_GetAttrString(varnames_obj, "co_kwonlyargcount");
    if (kwonly_count_obj && PyLong_Check(kwonly_count_obj)) {
        Py_ssize_t kwonly_count = PyLong_AsSsize_t(kwonly_count_obj);
        // 从arg_count的位置开始,取kwonly_count个元素作为关键字-only参数
        for (Py_ssize_t i = arg_count; i < arg_count + kwonly_count; ++i) {
            PyObject* arg_name_obj = PyTuple_GetItem(varnames_obj, i);
            if (arg_name_obj && PyUnicode_Check(arg_name_obj)) {
                const char* arg_name = PyUnicode_AsUTF8(arg_name_obj);
                if (arg_name) {
                    args.push_back(arg_name);
                }
            }
        }
        Py_DECREF(kwonly_count_obj);
    }

    Py_DECREF(varnames_obj);
    return args;
}

关键注意事项

  • 错误处理:上面的示例里加了基本的错误检查,但实际使用时要记得在每个PyObject*操作后检查是否为NULL,并且及时处理Python异常(比如用PyErr_Occurred()判断,PyErr_Print()打印,PyErr_Clear()清除)
  • 版本兼容性:co_kwonlyargcount是Python 3才有的属性,如果你要兼容Python 2,需要做版本判断;inspect.getfullargspec在Python 3.0+可用,替代了Python 2的inspect.getargspec
  • 对象生命周期:所有通过PyXXX_New、PyImport_ImportModule等获取的PyObject*,都要记得用Py_DECREF释放,避免内存泄漏

这两个方案都不需要解析.py或反编译.pyc,完全是通过Python运行时的API来获取参数名,不管函数是从.py还是.pyc加载的,都能正常工作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:40:15