gpt4 book ai didi

python - 如何在 Python C-API 中动态创建派生类型

转载 作者:IT老高 更新时间:2023-10-28 21:34:50 26 4
gpt4 key购买 nike

假设我们有 tutorial on writing C extension modules for Python 中定义的类型 Noddy .现在我们要创建一个派生类型,只覆盖 Noddy__new__() 方法。

目前我使用以下方法(为了可读性而去除错误检查):

PyTypeObject *BrownNoddyType =
(PyTypeObject *)PyType_Type.tp_alloc(&PyType_Type, 0);
BrownNoddyType->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
BrownNoddyType->tp_name = "noddy.BrownNoddy";
BrownNoddyType->tp_doc = "BrownNoddy objects";
BrownNoddyType->tp_base = &NoddyType;
BrownNoddyType->tp_new = BrownNoddy_new;
PyType_Ready(BrownNoddyType);

这可行,但我不确定这是否是正确的方法。我原以为我必须设置 Py_TPFLAGS_HEAPTYPE标志也是,因为我在堆上动态分配类型对象,但这样做会导致解释器中的段错误。

我还考虑过使用 PyObject_Call() 或类似方法显式调用 type(),但我放弃了这个想法。我需要将函数 BrownNoddy_new() 包装在 Python 函数对象中,并创建一个将 __new__ 映射到该函数对象的字典,这似乎很愚蠢。

解决此问题的最佳方法是什么?我的方法正确吗?有没有漏掉的界面功能?

更新

在 python-dev 邮件列表 (1) 上有两个相关主题的主题。 (2) .从这些线程和一些实验中,我推断我不应该设置 Py_TPFLAGS_HEAPTYPE,除非类型是通过调用 type() 分配的。这些线程中有不同的建议,无论是手动分配类型还是调用 type() 更好。如果我知道包装应该放在 tp_new 插槽中的 C 函数的推荐方法是什么,我会对后者感到满意。对于常规方法,这一步很容易——我可以使用 PyDescr_NewMethod()得到一个合适的包装对象。我不知道如何为我的 __new__() 方法创建这样的包装器对象——也许我需要未记录的函数 PyCFunction_New() 来创建这样的包装器对象。

最佳答案

我在修改扩展以兼容 Python 3 时遇到了同样的问题,并在尝试解决时发现了此页面。

我最终通过阅读 Python 解释器的源代码来解决它,PEP 0384以及 C-API 的文档.

设置 Py_TPFLAGS_HEAPTYPE 标志告诉解释器将您的 PyTypeObject 重铸为 PyHeapTypeObject,其中包含还必须分配的其他成员。在某些时候,解释器会尝试引用这些额外的成员,如果您不分配它们,则会导致段错误。

Python 3.2 引入了 C 结构 PyType_SlotPyType_Spec 以及简化动态类型创建的 C 函数 PyType_FromSpec。简而言之,您使用 PyType_SlotPyType_Spec 指定 PyTypeObjecttp_* 成员,然后调用 PyType_FromSpec 来完成分配和初始化内存的脏活。

从 PEP 0384 开始,我们有:

typedef struct{
int slot; /* slot id, see below */
void *pfunc; /* function pointer */
} PyType_Slot;

typedef struct{
const char* name;
int basicsize;
int itemsize;
int flags;
PyType_Slot *slots; /* terminated by slot==0. */
} PyType_Spec;

PyObject* PyType_FromSpec(PyType_Spec*);

(以上不是 PEP 0384 的文字副本,其中还包括 const char *doc 作为 PyType_Spec 的成员。但该成员没有出现在源代码中。)

要在原始示例中使用这些,假设我们有一个 C 结构 BrownNoddy,它扩展了基类 Noddy 的 C 结构。然后我们会:

PyType_Slot slots[] = {
{ Py_tp_doc, "BrownNoddy objects" },
{ Py_tp_base, &NoddyType },
{ Py_tp_new, BrownNoddy_new },
{ 0 },
};
PyType_Spec spec = { "noddy.BrownNoddy", sizeof(BrownNoddy), 0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots };
PyTypeObject *BrownNoddyType = (PyTypeObject *)PyType_FromSpec(&spec);

这应该完成原始代码中的所有事情,包括调用 PyType_Ready,以及创建动态类型所必需的,包括设置 Py_TPFLAGS_HEAPTYPE,以及分配和初始化额外的PyHeapTypeObject 的内存。

我希望这会有所帮助。

关于python - 如何在 Python C-API 中动态创建派生类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8066438/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com