如何在Python C API中使用多个模块/类型?

3 投票
1 回答
1010 浏览
提问于 2025-04-15 18:27

我有两个不同的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 个回答

3

你的问题是 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 * 就可以了。

撰写回答