使用 C API 和 `object` 的 `tp_basicsize` 从任意 Python class 派生?
Deriving from arbitrary Python class with C API and `tp_basicsize` of `object`?
我正在尝试定义一个函数,它将使用 C API 创建一个 Python class,派生自任意 Python 类型 base
,并且在其原始的类 C 对象布局中有一个额外的字段 void* my_ptr
。我希望它重用 Python 的 __dict__
功能。
我不是用 C 来做的,所以不能访问 C 宏。我最初的尝试看起来像这样(伪代码):
PyType Derive(PyType base) {
var newType = new PyType(...);
newType.tp_flags = HeapType | BaseType; // <- this is important,
// one should be able to add new attributes and otherwise use __dict__, subclass, etc
... filling in other things ...
int my_ptr_offset = base.tp_basesize; // put my_ptr immediately after base type data
newType.tp_basesize = my_ptr_offset + sizeof(void*); // instances of new type
// will have instance size = base size + size of my_ptr
...
return newType;
}
问题是当 base
为 builtins.object
时,此代码会崩溃。在那种情况下 tp_basesize
不计算通常存储 __dict__
的字段,并且 my_ptr_offset
最终指向该字段,最终导致它被 [=25 的消费者覆盖=].
任何从 object
派生的简单 Python class 都没有这个问题。例如:
class MySimpleClass: pass
在 64 位机器上:
PyType mySimpleClass = ...;
PyType object = ...;
mySimpleClass.tp_basesize // <- 32, includes __dict__
object.tp_basesize // <- 16, does not include space for __dict__
我也注意到 builtins.exception
也有类似的问题。
现在我只是手动检查 exception
和 object
并将 2x sizeof(void*)
添加到 tp_basesize
,这似乎有效。但我想了解如何正确处理该布局。
我想你要的信息在基地的tp_dictoffset
里。如果将其设置为 0,则基数没有 __dict__
,其他任何东西都有。
我有点不清楚你是如何创建你的类型的,但至少是通过调用 PyType_Type
(在 [=26= 中编写 class X:
时内部使用的方法]) 除非定义 __slots__
否则会添加一个 dict - 听起来这既是你想要发生的事情也是正在发生的事情。这在我链接的文档部分的 "Inheritance" 下有详细说明。
因此,如果 tp_dictoffset == 0
(假设您没有定义 __slots__
),则添加 sizeof(PyObject*)
以说明隐式添加的字典。
我正在尝试定义一个函数,它将使用 C API 创建一个 Python class,派生自任意 Python 类型 base
,并且在其原始的类 C 对象布局中有一个额外的字段 void* my_ptr
。我希望它重用 Python 的 __dict__
功能。
我不是用 C 来做的,所以不能访问 C 宏。我最初的尝试看起来像这样(伪代码):
PyType Derive(PyType base) {
var newType = new PyType(...);
newType.tp_flags = HeapType | BaseType; // <- this is important,
// one should be able to add new attributes and otherwise use __dict__, subclass, etc
... filling in other things ...
int my_ptr_offset = base.tp_basesize; // put my_ptr immediately after base type data
newType.tp_basesize = my_ptr_offset + sizeof(void*); // instances of new type
// will have instance size = base size + size of my_ptr
...
return newType;
}
问题是当 base
为 builtins.object
时,此代码会崩溃。在那种情况下 tp_basesize
不计算通常存储 __dict__
的字段,并且 my_ptr_offset
最终指向该字段,最终导致它被 [=25 的消费者覆盖=].
任何从 object
派生的简单 Python class 都没有这个问题。例如:
class MySimpleClass: pass
在 64 位机器上:
PyType mySimpleClass = ...;
PyType object = ...;
mySimpleClass.tp_basesize // <- 32, includes __dict__
object.tp_basesize // <- 16, does not include space for __dict__
我也注意到 builtins.exception
也有类似的问题。
现在我只是手动检查 exception
和 object
并将 2x sizeof(void*)
添加到 tp_basesize
,这似乎有效。但我想了解如何正确处理该布局。
我想你要的信息在基地的tp_dictoffset
里。如果将其设置为 0,则基数没有 __dict__
,其他任何东西都有。
我有点不清楚你是如何创建你的类型的,但至少是通过调用 PyType_Type
(在 [=26= 中编写 class X:
时内部使用的方法]) 除非定义 __slots__
否则会添加一个 dict - 听起来这既是你想要发生的事情也是正在发生的事情。这在我链接的文档部分的 "Inheritance" 下有详细说明。
因此,如果 tp_dictoffset == 0
(假设您没有定义 __slots__
),则添加 sizeof(PyObject*)
以说明隐式添加的字典。