如何在Python C-API中动态创建派生类型
假设我们具有中定义的类型如何在Python C-API中动态创建派生类型,python,derived-class,python-2.x,python-c-api,Python,Derived Class,Python 2.x,Python C Api,假设我们具有中定义的类型Noddy。现在我们要创建一个派生类型,只覆盖Noddy的\uuuuu new\uuuu()方法 目前我使用以下方法(检查可读性的错误): 这是可行的,但我不确定这样做是否正确。我也希望我必须设置标志,因为我在堆上动态分配type对象,但这样做会导致解释器中出现segfault 我还考虑过使用PyObject\u Call()或类似方法显式调用type(),但我放弃了这个想法。我需要将函数BrownNoddy\u new()包装在Python函数对象中,并创建一个映射到该
Noddy
。现在我们要创建一个派生类型,只覆盖Noddy
的\uuuuu new\uuuu()
方法
目前我使用以下方法(检查可读性的错误):
这是可行的,但我不确定这样做是否正确。我也希望我必须设置标志,因为我在堆上动态分配type对象,但这样做会导致解释器中出现segfault
我还考虑过使用PyObject\u Call()
或类似方法显式调用type()
,但我放弃了这个想法。我需要将函数BrownNoddy\u new()
包装在Python函数对象中,并创建一个映射到该函数对象的字典\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
最好的办法是什么?我的方法正确吗?有没有我错过的接口功能
更新
python开发人员邮件列表中有两个相关主题的线程。通过这些线程和一些实验,我推断我不应该设置Py\u TPFLAGS\u HEAPTYPE
,除非通过调用type()
来分配类型。无论是手动分配类型还是调用type()
,这些线程中都有不同的建议。如果我知道在tp_new
槽中包装C函数的推荐方法是什么,我会很高兴使用后者。对于常规方法,这一步很容易——我可以使用它来获得合适的包装器对象。我不知道如何为我的\uu new\uuu()
方法创建这样一个包装器对象——也许我需要使用未记录的函数PyCFunction\u new()
来创建这样一个包装器对象。尝试并理解如何做到这一点的一种方法是使用SWIG创建它的一个版本。看看它产生了什么,看看它是否匹配或以不同的方式完成。我可以告诉一直在编写SWIG的人,他们对扩展Python有着深刻的理解。不管怎样,看看他们是怎么做的都无妨。这可能有助于您理解这个问题。如果这个答案很糟糕,我很抱歉,但是您可以在中找到这个想法的实现,特别是我认为以下文件可能是有用的参考:
PythonQtClassWrapper_init中的这个片段让我觉得有点有趣:
static int PythonQtClassWrapper_init(PythonQtClassWrapper* self, PyObject* args, PyObject* kwds)
{
// call the default type init
if (PyType_Type.tp_init((PyObject *)self, args, kwds) < 0) {
return -1;
}
// if we have no CPP class information, try our base class
if (!self->classInfo()) {
PyTypeObject* superType = ((PyTypeObject *)self)->tp_base;
if (!superType || (superType->ob_type != &PythonQtClassWrapper_Type)) {
PyErr_Format(PyExc_TypeError, "type %s is not derived from PythonQtClassWrapper", ((PyTypeObject*)self)->tp_name);
return -1;
}
// take the class info from the superType
self->_classInfo = ((PythonQtClassWrapper*)superType)->classInfo();
}
return 0;
}
static int pythonqtclasswapper_init(pythonqtclasswapper*self,PyObject*args,PyObject*kwds)
{
//调用默认类型init
if(PyType_Type.tp_init((PyObject*)self,args,kwds)<0){
返回-1;
}
//如果没有CPP类信息,请尝试我们的基类
如果(!self->classInfo()){
PyTypeObject*超类型=((PyTypeObject*)self)->tp_基;
if(!superType | |(superType->ob_type!=&PythonQtClassWrapper_type)){
PyErr_格式(PyExc_TypeError,“类型%s不是从pythonqtclasswapper派生的”,((PyTypeObject*)self)->tp_名称);
返回-1;
}
//从超类型获取类信息
self->_classInfo=((PythonQtClassWrapper*)超类型)->classInfo();
}
返回0;
}
值得注意的是,PythonQt确实使用了一个包装生成器,因此它并不完全符合您的要求,但我个人认为,试图智胜vtable并不是最理想的设计。基本上,Python有许多不同的C++包装生成器,人们用它们来做一个很好的理由——它们被记录下来,在搜索结果和堆栈溢出中到处都有例子。如果你手推一个以前没人见过的解决方案,那么如果他们遇到问题,调试就会困难得多。即使它是封闭源代码,下一个必须维护它的人也会挠头,你必须向每个新来的人解释
< >一旦你得到一个代码生成器,你所需要做的就是维护底层C++代码,你不必手工更新或修改扩展代码。(这可能与您所采用的诱人解决方案相差不远)
建议的解决方案是打破新引入的类型安全的一个例子(当按照指示使用时)
因此,虽然这样实现派生类/子类可能不是最好的长期选择,但应该将代码包装起来,让vtable做它最擅长的事情,当新成员有问题时,您可以向他指出文档
不过这只是我的看法D我在修改扩展以与Python 3兼容时遇到了相同的问题,并在尝试解决该问题时找到了此页面
我最终通过阅读Python解释器的源代码和
设置Py\u TPFLAGS\u HEAPTYPE
标志会告诉解释器将PyTypeObject
重新编译为PyHeapTypeObject
,其中包含还必须分配的其他成员。在某个时刻,解释器试图引用这些额外的成员,如果您让它们未分配,则会导致segfault
Python 3.2引入了简化动态类型创建的C结构PyType_Slot
和PyType_Spec
以及C函数PyType_FromSpec
。简而言之,您可以使用PyType\u Slot
和PyType\u Spec
来指定PyTypeObject
的tp*
成员,然后从Spec
调用PyType\u来完成分配和初始化内存的繁琐工作
根据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
的成员。但该成员未出现在源代码中。)
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*);
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);