You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Python C++扩展中将Python属性绑定到C++变量?

如何将C++变量直接绑定为Python模块属性

当然可以实现!你想要的这种直接通过hello.a读写C变量的方式完全可行,只需要给你的Python模块添加属性访问器(getter/setter),替换掉原来的get_a/set_a方法(也可以保留方法同时支持属性访问)。咱们一步步来修改你的C包装代码:

1. 定义属性的访问函数

首先,我们需要为属性a编写对应的获取和设置函数,替代原来的get_aset_a

// 属性getter:获取ex.a的值
static PyObject* example_get_a(PyObject* self, void* closure) {
    return PyFloat_FromDouble(ex.a); // 把C++ float转成Python的float对象
}

// 属性setter:设置ex.a的值
static int example_set_a(PyObject* self, PyObject* value, void* closure) {
    if (!PyFloat_Check(value)) { // 检查传入的是不是float类型
        PyErr_SetString(PyExc_TypeError, "Expected a float");
        return -1;
    }
    ex.a = (float)PyFloat_AsDouble(value); // 把Python float转成C++ float
    return 0; // 返回0表示设置成功
}

2. 创建模块属性列表

PyGetSetDef结构体把属性名和对应的访问函数关联起来:

static PyGetSetDef hello_getsetters[] = {
    {"a", example_get_a, example_set_a, "The value of a", NULL},
    {NULL} // 数组末尾必须用NULL结束
};

每个字段的含义:

  • 第一个字符串是Python端可见的属性名(这里就是a
  • 第二个是属性的getter函数
  • 第三个是属性的setter函数
  • 第四个是属性的文档字符串
  • 最后一个是closure参数(这里用不到,传NULL即可)

3. 修改模块定义

把原来PyModuleDef里的方法列表和新增的属性列表关联起来(如果想保留原来的get_a/set_a方法,可以同时保留m_methodsm_getset字段):

static struct PyModuleDef hello_definition = {
    PyModuleDef_HEAD_INIT,
    "hello",
    "A Python module that binds a C++ variable as a module attribute.",
    -1,
    hello_methods, // 可选:保留原来的方法
    hello_getsetters, // 添加属性访问器
    NULL,
    NULL,
    NULL
};

完整修改后的C++代码

#include <Python.h>

/* EXAMPLE OF A CLASS WITH VALUE */
class example {
public:
    float a;
    example(float a_init);
};
example::example(float a_init){
    this->a = a_init;
}
example ex(1.0);

// 属性访问函数
static PyObject* example_get_a(PyObject* self, void* closure) {
    return PyFloat_FromDouble(ex.a);
}

static int example_set_a(PyObject* self, PyObject* value, void* closure) {
    if (!PyFloat_Check(value)) {
        PyErr_SetString(PyExc_TypeError, "Expected a float");
        return -1;
    }
    ex.a = (float)PyFloat_AsDouble(value);
    return 0;
}

// 属性列表
static PyGetSetDef hello_getsetters[] = {
    {"a", example_get_a, example_set_a, "The value of a", NULL},
    {NULL}
};

/* 可选:保留原来的方法(如果需要) */
static PyObject* get_a(PyObject *self, PyObject *args);
static PyObject* set_a(PyObject *self, PyObject *args);
static PyMethodDef hello_methods[] = {
    {"get_a", get_a, METH_VARARGS,"Get the value of a",},
    {"set_a", set_a, METH_VARARGS,"Set the value of a",},
    {NULL, NULL, 0, NULL}
};

/* MODULE */
static struct PyModuleDef hello_definition = {
    PyModuleDef_HEAD_INIT,
    "hello",
    "A Python module that binds a C++ variable as a module attribute.",
    -1,
    hello_methods, // 保留方法(可选)
    hello_getsetters, // 添加属性访问器
    NULL,
    NULL,
    NULL
};

/* INIT MODULE */
PyMODINIT_FUNC PyInit_hello(void) {
    return PyModule_Create(&hello_definition);
}

// 可选:保留原来的方法实现
static PyObject* get_a(PyObject *self, PyObject *args) {
    return Py_BuildValue("f", ex.a);
}
static PyObject* set_a(PyObject *self, PyObject *args) {
    float value;
    if (!PyArg_ParseTuple(args, "f", &value))
        return NULL;
    ex.a = value;
    Py_RETURN_NONE;
}

测试Python代码

现在你就可以用你想要的简洁方式访问属性了:

import hello
if __name__ == "__main__":
    # GET THE VALUE OF 'a'
    print('the value of \'a\' is:', hello.a)
    # SET A VALUE FOR 'a'
    hello.a = 5.0
    # GET THE VALUE OF 'a'
    print('the value of \'a\' is:', hello.a)
    # 测试错误类型(可选)
    try:
        hello.a = "not a float"
    except TypeError as e:
        print("Error:", e)

运行结果

the value of 'a' is: 1.0
the value of 'a' is: 5.0
Error: Expected a float

这样就完美实现了你想要的直接绑定效果,还额外添加了类型检查让代码更健壮!

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

火山引擎 最新活动