如何在Python C API中使用多个模块/类型?
我有两个不同的Python扩展模块,叫它们A和B。模块A里面有一个叫做container的存储类,我想在模块B中把它作为一个类方法的返回类型使用。
我找不到任何关于怎么做的文档。我大致按照这篇文章的指导创建了模块和类,除了我没有把所有的方法都声明为静态方法,这样它们就可以被访问了:http://nedbatchelder.com/text/whirlext.html
我的问题是,我该如何创建一个container的实例,以便可以作为模块B中类方法的PyObject*返回值?container的定义看起来是这样的:
typedef struct {
PyObject_HEAD
storageclass* cnt_;
} container;
我尝试在相关的方法中简单地这样做,其中container_init是我为container类注册的tp_init方法:
pycnt::container* retval;
pycnt::container_init(retval, NULL, NULL);
return (PyObject*)retval;
但是根据Python解释器的反馈,我得到的是我调用该方法的类的实例。(也就是说,myclassinstance.mymethod()返回的是myclassinstance)。
显然,我的做法是错的,但我不知道正确的做法是什么。有什么建议吗?为了避免有人建议,我不想使用SWIG或Boost::Python。我已经尝试过了,container的底层存储类与这两者都不兼容(SWIG甚至无法解析它)。到目前为止,我自己做扩展还算顺利,但在这个问题上我真的卡住了。
1 个回答
你的问题是 type->tp_init
并没有按照你想要的方式工作。type->tp_init
是在你分配了一个对象后被调用,用来进行初始化。你首先需要用 type->tp_new
来分配这个对象,然后把这个对象作为第一个参数传给 type->tp_init
。因为你传给 tp_init
的是一个未初始化的指针,所以结果就不确定了。通常情况下,你不会直接调用这两个函数,而是会在类型对象上调用 PyObject_Call*()
来创建一个新的实例。或者你可以提供一个普通的 C 函数来创建新的实例,比如 PyFoo_New()
。
另外,两个扩展模块之间的常见通信方式是通过 Python API。如果你想让模块 B 导入并使用模块 A,你需要调用 PyImport_Import()
来获取模块对象,然后用 PyObject_GetAttrString()
来获取你关心的类型对象,最后用 PyObject_Call*()
来实例化它。在你想存储那个类型的指针的地方,你只需要使用 PyObject *
就可以了。