如何从C创建一个实现__dict__的Python类型?
- 如何创建一个类型,使它有一个像普通类那样的
__dict__
,就像在Python中定义的类一样? - 有没有例子是非动态类型却有
__dict__
的? - 通过Python的
PyTypeObject
定义的类型是否会经过type_new
?
在 PyTypeObject
中有一个 tp_dict
成员,但我找不到关于它如何使用的信息。在 typeobject.c
的 type_new
中似乎也有一些事情发生,但我无法清楚地理解。
这是我找到的一些相关信息:
4 个回答
我没做过这个,实在是对C-API的使用很不在行,不过根据文档的说法,使用 PyObject_GenericGetAttr
和 PyObject_GenericSetAttr
来实现 getattro
和 setattro
方法应该就够了。当然,你还需要有一个叫 __dict__
的 PyObject 属性。
你试过这样做吗?
首先回答最后一个问题:不,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__
并传入适当的参数。
下面的代码会生成一个在 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
混淆了;后者是类型对象本身的属性字典。