如何从C创建一个实现__dict__的Python类型?

8 投票
4 回答
2875 浏览
提问于 2025-04-16 12:11
  • 如何创建一个类型,使它有一个像普通类那样的 __dict__,就像在Python中定义的类一样?
  • 有没有例子是非动态类型却有 __dict__ 的?
  • 通过Python的 PyTypeObject 定义的类型是否会经过 type_new

PyTypeObject 中有一个 tp_dict 成员,但我找不到关于它如何使用的信息。在 typeobject.ctype_new 中似乎也有一些事情发生,但我无法清楚地理解。

这是我找到的一些相关信息:

4 个回答

1

我没做过这个,实在是对C-API的使用很不在行,不过根据文档的说法,使用 PyObject_GenericGetAttrPyObject_GenericSetAttr 来实现 getattrosetattro 方法应该就够了。当然,你还需要有一个叫 __dict__ 的 PyObject 属性。

你试过这样做吗?

2

首先回答最后一个问题:不,type_new 只用于那些在运行时动态定义的“堆类型”(比如通过类声明定义的类型)。而静态定义的类型则是通过 PyType_Ready() 来初始化的。

接下来回答你的第一个问题:如果你想创建一个带有 __dict__ 描述符的扩展类型,你需要以解释器为类定义动态分配类型。

想要获取这方面的例子,可以按照约翰的建议,使用 Cython 自己生成一些示例。

如果你在使用 CPython 2.x,可以查看 CPython 源代码中的 build_class 方法(http://svn.python.org/view/python/trunk/Python/ceval.c?view=markup),这样可以了解实现一个通用解决方案所需的步骤。

不过,如果你使用的是 Python 3,那么这个问题可能会引起你的兴趣:Python 的内置 __build_class__ 是做什么的?

也就是说,作为 CPython 3.x 的特定解决方案,最简单的方法是通过 C API 调用 builtins.__build_class__ 并传入适当的参数。

6

下面的代码会生成一个在 Python 2.x 中实现 __dict__ 的类:

typedef struct {
  PyObject_HEAD
  PyObject* dict;
} BarObject;

static PyTypeObject BarObject_Type = {
  PyObject_HEAD_INIT(NULL)
};

PyMODINIT_FUNC
initFoo(void)
{
  PyObject *m;

  m = Py_InitModule("Foo", NULL);
  if (m == NULL)
    return;

  BarObject_Type.tp_new = PyType_GenericNew;
  BarObject_Type.tp_name = "Foo.Bar";
  BarObject_Type.tp_basicsize = sizeof(BarObject);
  BarObject_Type.tp_getattro = PyObject_GenericGetAttr;
  BarObject_Type.tp_setattro = PyObject_GenericSetAttr;
  BarObject_Type.tp_flags = Py_TPFLAGS_DEFAULT;
  BarObject_Type.tp_dictoffset = offsetof(BarObject,dict);
  BarObject_Type.tp_doc = "Doc string for class Bar in module Foo.";
  if (PyType_Ready(&BarObject_Type) < 0)
    return;

  Py_INCREF(&BarObject_Type);
  PyModule_AddObject(m, "Bar", (PyObject*)&BarObject_Type);
}

这里面最重要的是 PyTypeObject 结构体中的 tp_dictoffset 成员(你可以在这个链接找到更多信息:http://docs.python.org/c-api/typeobj.html):

如果这个类型的实例有一个包含实例变量的字典,那么这个字段的值就不是零,它会包含这个类型实例中实例变量字典的偏移量;这个偏移量会被 PyObject_GenericGetAttr() 使用。

不要把这个字段和 tp_dict 混淆了;后者是类型对象本身的属性字典。

撰写回答