在Python扩展中将底层结构暴露为自定义类型的成员

5 投票
1 回答
637 浏览
提问于 2025-04-16 23:29

这个问题是对之前一个问题的稍微改动:访问PyObject的底层结构

不过在我的版本中,我想知道如何将Point结构体的字段作为我新类型的成员来展示。

我到处查找过,读了很多示例的Python扩展,也看了不少教程和文档,但就是找不到一个简单的方法来实现这个。

下面是我想做的一个例子。我有一个内部结构体,想通过Python扩展来展示它,但我不想在我的代码中重新定义这个结构体。看起来主要的问题在于PyMemeberDef的定义……在PointObject结构体的上下文中,x或y在Point结构体中的偏移量应该是多少呢?

任何帮助都非常感谢,谢谢。

#include <Python.h>
#include <structmember.h>

// This is actually defined elsewhere in someone else's code.
struct Point {
 int x;
 int y;
};

struct PointObject {
 PyObject_HEAD
 struct Point* my_point;
 int z;
};

static PyMemberDef point_members[] = {
    {"z", T_INT, offsetof(struct PointObject, z), 0, "z field"},
    {"x", T_INT, offsetof(???), 0, "point x field"},
    {"y", T_INT, offsetof(???), 0, "point y field"},
    {NULL}
};

static PyTypeObject PointType = {
    PyObject_HEAD_INIT(NULL)
    .tp_name = "Point",
    .tp_basicsize = sizeof(PointObject),
    .tp_flags = Py_TPFLAGS_DEFAULT,
    .tp_doc = "Point objects",
    .tp_members = point_members,
    .tp_init = (initproc)point_init,
};

...

PyMODINIT_FUNC
initMainModule(void)
{
    PyObject *m = Py_InitModule(MODULE, NULL);

    // Register PointType
    PointType.tp_new = PyType_GenericNew;
    if (PyType_Ready(&PointType) < 0)
            return;
    Py_INCREF(&PointType);
    PyModule_AddObject(m, "Point", (PyObject *)&PointType);

    ...
}

1 个回答

2

我强烈建议你去看看这个链接:http://docs.python.org/release/2.6/_sources/extending/newtypes.txt,特别是里面关于“更细致控制数据属性”的部分。

看完之后,我发现可以通过为Point结构体里的特定成员定义一组设置器(setter)和获取器(getter)来实现这个功能。虽然对于这么简单的事情来说,这种做法有点复杂,但似乎是可以正常工作的。

下面是一个关于如何为Point的'x'字段实现这个功能的例子,基于我之前的代码添加了以下内容:

static PyGetSetDef point_getset[] = {
  {"x", (getter)point_get_x, (setter)point_set_x, "", NULL},
  {NULL}
};

static PyObject*
point_get_x(struct PointObject *self, void *closure)
{
     return Py_BuildValue("i", self->my_point->x);
}

static int
point_set_x(struct PointObject *self, PyObject *value, void *closure)
{
  PyErr_SetString(PyExc_TypeError, "Attribute is read-only!");
  return -1;
}

最后,把point_getset结构体添加到之前定义的PyTypeObject结构体中的tp_getset字段里。需要注意的是,对于简单的情况,你仍然可以在传统的PyMemberDef结构体中定义成员。

撰写回答