衍生自具有C API和object'tp_basicsize`的任意Python类?

问题描述 投票:1回答:2

我正在尝试定义一个函数,该函数将使用C API创建Python类,该函数派生自任意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;
}

问题是basebuiltins.object时此代码损坏。在那种情况下,tp_basesize不对字段进行计数,该字段通常会存储__dict__,并且my_ptr_offset最终指向该字段,最终导致my_ptr的使用者将其覆盖。

object派生的任何简单Python类都没有这个问题。例如:

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的类似问题。

现在,我只是手动检查exceptionobject,然后将2x sizeof(void*)添加到tp_basesize,这似乎可行。但我想了解如何正确处理该布局。

python python-c-api
2个回答
0
投票

我认为您想要的信息位于基准的tp_dictoffset中。如果将其设置为0,则该基数没有tp_dictoffset,其他任何东西都没有。

我对如何创建类型尚不清楚,但是至少要通过调用__dict__(在Python中编写PyType_Type时在内部使用的方法)来添加字典,除非class X:为定义-听起来这既是您要发生的事情,又是正在发生的事情。在我链接的文档的“继承”部分中对此进行了详细说明。

因此,如果__slots__(并假设您没有定义tp_dictoffset == 0,则将__slots__添加到隐式添加的字典中。


0
投票

没有通过Python C API做到这一点的安全方法。除了通过sizeof(PyObject*)以外,绝不能以其他任何方式创建堆类型。新堆类型的初始设置非常复杂且与版本有关,其中包含许多未记录的详细信息。 type.__new__在CPython 3.8.2中超过500行,不包括辅助函数。

© www.soinside.com 2019 - 2024. All rights reserved.