我正在用 C++ 编写 Python 3 扩展,并试图找到一种方法来检查 aPyObject
是否与定义其实例布局的类型(结构)相关。我只对 static-size 感兴趣PyObject
,而不是PyVarObject
. 实例布局由具有某些明确定义的布局的结构定义:强制PyObject
标头和(可选)用户定义的成员。
下面是PyObject
基于定义新类型中著名的Noddy 示例的扩展示例:
// Noddy struct specifies PyObject instance layout
struct Noddy {
PyObject_HEAD
int number;
};
// type object corresponding to Noddy instance layout
PyTypeObject NoddyType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"noddy.Noddy", /*tp_name*/
sizeof(Noddy), /*tp_basicsize*/
0, /*tp_itemsize*/
...
Noddy_new, /* tp_new */
};
重要的是要注意它Noddy
是一个类型,一个编译时实体,但NoddyType
它是一个在运行时存在于内存中的对象。Noddy
和之间唯一明显的关系NoddyType
似乎是sizeof(Noddy)
存储在tp_basicsize
成员中的值。
在 Python 中实现的手写继承指定了允许在PyObject
用于声明该特定实例布局的类型之间进行转换的规则PyObject
:
PyObject* Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
// When a Python object is a Noddy instance,
// its PyObject* pointer can be safely cast to Noddy
Noddy *self = reinterpret_cast<Noddy*>(type->tp_alloc(type, 0));
self->number = 0; // initialise Noddy members
return reinterpret_cast<PyObject*>(self);
}
在各种槽函数等情况下,可以安全地假设“Python 对象是 Noddy”并在没有任何检查的情况下进行强制转换。但是,有时需要在其他情况下强制转换,然后感觉就像是在盲目转换:
void foo(PyObject* obj)
{
// How to perform safety checks?
Noddy* noddy = reinterpret_cast<Noddy*>(obj);
...
}
可以检查sizeof(Noddy) == Py_TYPE(obj)->tp_basicsize
,但由于以下原因,它是不充分的解决方案:
1) 如果用户将来自Noddy
class BabyNoddy(Noddy):
pass
并且指向 , 的实例是obj
不同的。但是,强制转换以获取指向实例布局部分的指针仍然是安全的。foo
BabyNoddy
Py_TYPE(obj)->tp_basicsize
reinterpret_cast<Noddy*>(obj)
2)可以有其他结构声明实例布局的大小相同Noddy
:
struct NeverSeenNoddy {
PyObject_HEAD
short word1;
short word2;
};
事实上,在 C 语言级别,NeverSeenNoddy
struct 与 object 类型兼容NoddyType
——它可以适应NoddyType
. 所以,演员阵容可能非常好。
所以,我的大问题是:
是否有任何 Python 策略可用于确定 aPyObject
是否与Noddy
实例布局兼容?
有什么方法可以检查是否PyObject*
指向嵌入的对象部分Noddy
?
如果没有政策,是否有任何黑客可能?
编辑:有几个问题似乎相似,但在我看来,它们与我提出的问题不同。例如:访问 PyObject 的底层结构
EDIT2:为了理解为什么我将 Sven Marnach 的回复标记为答案,请参阅该答案下方的评论。