如何确定用于声明 PyObject 实例布局的结构体?

mlo*_*kot 4 python api python-3.x pyobject

我正在用 C++ 编写 Python 3 扩展,并且正在尝试找到一种方法来检查 a 是否PyObject与定义其实例布局的类型(结构)相关。我只对 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 */
};
Run Code Online (Sandbox Code Playgroud)

值得注意的是,它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);
}
Run Code Online (Sandbox Code Playgroud)

在诸如各种槽函数之类的情况下,可以安全地假设“Python 对象是 Noddy”并在不进行任何检查的情况下进行强制转换。但是,有时候需要在其他情况下进行强制转换,那就感觉像是盲目转换:

void foo(PyObject* obj)
{
    // How to perform safety checks?
    Noddy* noddy = reinterpret_cast<Noddy*>(obj);
    ...
}
Run Code Online (Sandbox Code Playgroud)

可以检查sizeof(Noddy) == Py_TYPE(obj)->tp_basicsize,但这不是足够的解决方案,因为:

1)如果用户将从Noddy

class BabyNoddy(Noddy):
    pass
Run Code Online (Sandbox Code Playgroud)

objfoo实例BabyNoddy不同Py_TYPE(obj)->tp_basicsize。但是,转换为reinterpret_cast<Noddy*>(obj)以获取指向实例布局部分的指针仍然是安全的。

2) 可以有其他结构体声明与以下大小相同的实例布局Noddy

struct NeverSeenNoddy {
    PyObject_HEAD
    short word1;
    short word2;
};
Run Code Online (Sandbox Code Playgroud)

事实上,在 C 语言级别,NeverSeenNoddystruct 与类型对象兼容NoddyType- 它可以适合NoddyType. 所以,演员阵容可能完全没问题。

所以,我的大问题是:

是否有任何 Python 策略可用于确定 a 是否PyObject与实例布局兼容Noddy

有什么方法可以检查是否PyObject*指向嵌入在 中的对象部分Noddy

如果没有政策,是否有可能进行黑客攻击?

编辑:有几个问题看起来很相似,但在我看来它们与我问的问题不同。例如:访问 PyObject 的底层结构

EDIT2:为了理解为什么我将 Sven Marnach 的回复标记为答案,请参阅该答案下面的评论。

Sve*_*ach 5

在 Python 中,您可以使用 test 来检查 isobj是类型Noddy还是派生类型isinstance(obj, Noddy)。C-API 中的测试是否某些PyObject *obj类型NoddyType或派生类型基本相同,您使用PyObject_IsInstance()

PyObject_IsInstance(obj, &NoddyType)
Run Code Online (Sandbox Code Playgroud)

至于你的第二个问题,没有办法实现这一点,如果你认为你需要这个,那么你的设计就有严重的缺陷。最好首先NeverSeenNoddyTypeNoddyType派生 - 那么上面的检查还将把派生类型的对象识别为 的实例NoddyType