python动态类型如何用c语言实现,如何在Python C-API中动态创建派生类型

Assume we have the type Noddy as defined in the tutorial on writing C extension modules for Python. Now we want to create a derived type, overwriting only the __new__() method of Noddy.

Currently I use the following approach (error checking stripped for readability):

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);

This works, but I'm not sure if it is The Right Way To Do It. I would have expected that I have to set the Py_TPFLAGS_HEAPTYPE flag, too, because I dynamically allocate the type object on the heap, but doing so leads to a segfault in the interpreter.

I also thought about explicitly calling type() using PyObject_Call() or similar, but I discarded the idea. I would need to wrap the function BrownNoddy_new() in a Python function object and create a dictionary mapping __new__ to this function object, which seems silly.

What is the best way to go about this? Is my approach correct? Is there an interface function I missed?

Update

There are two threads on a related topic on the python-dev mailing list (1) (2). From these threads and a few experiments I deduce that I shouldn't set Py_TPFLAGS_HEAPTYPE unless the type is allocated by a call to type(). There are different recommendations in these threads whether it is better to allocate the type manually or to call type(). I'd be happy with the latter if only I knew what the recommended way to wrap the C function that is supposed to go in the tp_new slot is. For regular methods this step would be easy -- I could just use PyDescr_NewMethod() to get a suitable wrapper object. I don't know how to create such a wrapper object for my __new__() method, though -- maybe I need the undocumented function PyCFunction_New() to create such a wrapper object.

解决方案

I encountered the same problem when I was modifying an extension to be compatible with Python 3, and found this page when I was trying to solve it.

I did eventually solve it by reading the source code for the Python interpreter, PEP 0384 and the documentation for the C-API.

Setting the Py_TPFLAGS_HEAPTYPE flag tells the interpreter to recast your PyTypeObject as PyHeapTypeObject, which contains additional members that must also be allocated. At some point the interpreter attempts to refer to these extra members and, if you leave them unallocated, it will cause a segfault.

Python 3.2 introduced the C structures PyType_Slot and PyType_Spec and the C function PyType_FromSpec that simplify the creation of dynamic types. In a nutshell, you use PyType_Slot and PyType_Spec to specify the tp_* members of the PyTypeObject and then call PyType_FromSpec to do the dirty work of allocating and initialising the memory.

From PEP 0384, we have:

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*);

(The above isn't a literal copy from PEP 0384, which also includes const char *doc as a member of PyType_Spec. But that member doesn't appear in the source code.)

To use these in the original example, assume we have a C structure, BrownNoddy, that extends the C structure for the base class Noddy. Then we would have:

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);

This should do everything in the original code, including calling PyType_Ready, plus what is necessary for creating a dynamic type, including setting Py_TPFLAGS_HEAPTYPE, and allocating and initialising the extra memory for a PyHeapTypeObject.

I hope that's helpful.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值