如何在 cython 模块中使用外部包装类?
Posted
技术标签:
【中文标题】如何在 cython 模块中使用外部包装类?【英文标题】:How do I use an external wrapped class in cython module? 【发布时间】:2017-04-05 09:29:50 【问题描述】:我有一个已经包装好的外部类(我的意思是,它可以通过 python 直接访问,无需进一步努力),现在我希望它成为更大的 cython 模块的 part(在换句话说,嵌入它)。
我可以明确地 python 导入它。但问题是外部类已经在 cython 模块中的 extern
函数中使用(因此该类最终在源代码中为 #include
d)。 Python import 需要编译模块,那么这两个模块可能有同一个类的两个不同副本...
我应该如何在 cython 中使用外部已经包装的类?
(可能过于简单化)示例:
Foo.cpp:
#include "Python.h"
#include "foo.hpp"
struct Foo_wrapper
PyObject_HEAD
foo bar;
;
static int Foo_init(Foo_wrapper* self, PyObject* args, PyObject*)
....
static PyTypeObject FooType ...;
垃圾邮件.pyx:
cdef extern from "some_functions.hpp":
cdef some_function1(some_type); // this returns a wrapped foo object
def spam(arg1, arg2, arg3):
// wrap arg1, arg2, arg3 to some_type
return some_function1(an_instance_of_some_type); // huh? but foo isn't available here!
我想在spam.pyx
中使用 foo 类。
【问题讨论】:
我认为 Python 导入是对的。我认为没有问题,除非两个定义最终不同(即编译模块 1,更改类,编译模块 2)。不过,这个问题可以用一个最小的完整示例来解决。 @DavidW,我不认为直接导入是正确的。但如果你这么说,我会试一试。我不认为因为你定义了两个空类 Foo 和 Bar,那么 Foo 与 Bar 是同一个逻辑类,但对解释器来说不是。或者如果你在两个不同的模块中定义了两个同名的类,它们仍然不一样。 这就是为什么一些代码的简单示例会有所帮助的原因。我不是 100% 清楚你实际上在做什么! @DavidW,请查看添加的示例。 这说明了很多事情。让我考虑一下。 【参考方案1】:这应该没问题(几乎)。 cdef extern
这行不太对:
cdef extern from "some_functions.hpp":
object some_function1(some_type); // this returns a wrapped foo object
注意对object
的更改 - 这告诉 Cython 该函数返回一个 Python 对象。 C/C++ 声明如下所示:
PyObject* some_function1(some_type);
// or
Foo_wrapper* some_function1(some_type);
两者都行。
Cython 代码可以使用它的原因是 PyObject_HEAD
包含一个指向 PyTypeObject FooType
的指针 ob_type
。这是在创建对象时设置的。 PyTypeObject
包含 Python 解释器使用返回的对象所需的所有详细信息,因此一切正常。
整个东西基本上相当于Python代码:
# in "somemodule.py"
def a_function():
import something_else
return something_else.Class()
Python 解释器可以使用返回的值,尽管 Class
在“全局”命名空间中不为人所知。
需要注意的一件事是,在创建Foo_wrapper
之前,您应该确保至少调用了Foo
模块初始化函数一次。原因是这个函数通常会做一些事情,比如调用PyType_Ready(&FooType)
,以确保正确设置FooType
。一个简单的方法是将以下行添加到some_function1
:
PyObject* m = PyImport_ImportModule("Foo");
if (m==NULL) return NULL; // an error
Py_CLEAR(m); // don't need to keep a reference to it
不过,还有其他方法可以做同样的事情。
【讨论】:
根据我对您的回答的理解,(如果我错了,请纠正我),我只需要 注册 类型以便解释器识别它。而注册是通过PyType_Ready
完成的,一旦函数被调用,解释器就会知道类型。此外,如果本机完成,该函数应该在 cython 模块 init 函数中调用。对吗?
基本正确。如果 Foo.cpp
已经编译为 Python 模块,那么我认为在某处(可能在 some_function1
或从 Cython 中)进行模块导入比自己调用 PyType_Ready
更干净。如果 Foo.cpp
永远不会被编译成 Python 模块,那么从 Cython 模块的 init 函数调用 PyType_Ready
(即在 .pyx 文件的全局范围内调用它)就可以了。
是否可以用 init 将其理想地包装为实际类?或者我将不得不使用一些工厂功能。 (是的,在这个例子中 some_function1 是一个工厂函数。)
您将a tp_init
function 添加到TypeObject
是的,我有这个功能,但无法从模块中访问它。我只调用了 PyType_Ready,并没有将它添加到模块命名空间中。不过,我设法从 python 端添加了它。谢谢。以上是关于如何在 cython 模块中使用外部包装类?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 python 包装中使用 unicode 字符串用于带有 cython 的 c++ 类?