【问题标题】:"Cannot convert Python object argument to type '<typename>'" - error while wrapping c++-classes with Cython“无法将 Python 对象参数转换为类型 '<typename>'”- 使用 Cython 包装 c++ 类时出错
【发布时间】:2018-09-10 17:23:18
【问题描述】:

我正在尝试运行一个 cython 示例,它比许多教程中的示例(例如 this guide )要复杂一些。

这是一个最小的示例(请不要介意它没有太多功能)和重现我的问题的步骤:

有 c++-classesRectangleGroup2(我把这里的所有东西都放到了 .h 文件中以使其更短):

// Rectangle.h
namespace shapes { 
    class Rectangle {
        public:
            Rectangle() {}
    };

    class Group2 {
    public:
        Group2(Rectangle rect0, Rectangle rect1) {}
    };
}

然后我创建一个 grp2.pyx 文件(在与上述标题相同的文件夹中),其中包含 RectangleGroup2 的包装器:

# RECTANGLE
cdef extern from "Rectangle.h" namespace "shapes":
    cdef cppclass Rectangle:
        Rectangle() except +

cdef class PyRectangle:
    cdef Rectangle c_rect
    def __cinit__(self):
        self.c_rect = Rectangle()

# GROUP2
cdef extern from "Rectangle.h" namespace "shapes":
    cdef cppclass Group2:
        Group2(Rectangle rect0, Rectangle rect1) except +    

cdef class PyGroup2:
    cdef Group2 c_group2
    def __cinit__(self, Rectangle rect0, Rectangle rect1):
        self.c_group2 = Group2(rect0, rect1)

扩展是通过我从命令行 (python setup.py build_ext -i) 调用的setup.py 文件构建的:

from distutils.core import setup, Extension
from Cython.Build import cythonize

setup(ext_modules = cythonize(Extension(
           name="grp2",                                # the extension name
           sources=["grp2.pyx"], # the Cython source 
           language="c++",                        # generate and compile C++ code
      )))

此时我在PyGroup2_cinint_ 中有错误:

无法将 Python 对象参数转换为类型“矩形”

我想我的 pyx 文件中有一些错误,但我不知道是什么。

【问题讨论】:

    标签: python c++ cython


    【解决方案1】:

    在将矩形传递给 C++ 函数时,您应该在 def-functions 和 PyRectangle.c_rect 的签名中使用 PyRectangle

    这意味着您的代码应该是:

    cdef class PyGroup2:
        ...
        def __cinit__(self, PyRectangle rect0, PyRectangle rect1):
            self.c_group2 = Group2(rect0.c_rect, rect1.c_rect)
    

    继续阅读以获得更详细的解释。


    所有传递给def-functions 的参数都是Python-objects(即在Cython-parlance 中的object 类型),因为所有这些函数都将从只知道Python-objects 的纯Python 调用。

    但是,您可以添加一些语法糖,并在 def 函数的签名中使用“后期绑定”,例如,而不是

    def do_something(n):
      ...
    

    使用

    def do_something(int n):
      ...
    

    在后台,Cython 会将此代码转换为:

    def do_something(n_):
       cdef int n = n_ # conversion to C-int
       ...
    

    这种自动转换对于像 intdouble 这样的内置类型是可能的,因为 Python-C-API 中有用于这些转换的功能(即 PyLong_AsLongPyFloat_AsDouble)。 Cython 还处理错误检查,因此您不应手动进行这些转换。

    但是,对于像 Rectangle-class 这样的用户定义类型/类,这种自动转换是不可能的 - Cython 只能自动转换为 cdef-classes/extensions,即 PyRectangle,因此 PyRectangle 应该在签名中使用:

    cdef class PyGroup2:
        ...
        def __cinit__(self, PyRectangle rect0, PyRectangle rect1):
            ...
    

    在 Cython 完成从 objectPyRectangle 的转换之后,必须使用 c_rect 手动执行从 PyRectangleRectangle 的最后一步 - 指针:

    ...
    def __cinit__(self, PyRectangle rect0, PyRectangle rect1):
        self.c_group2 = Group2(rect0.c_rect, rect1.c_rect)
    

    cpdef-function 的规则类似,因为它们可以从纯 Python 中调用。 "early binding" 仅适用于 Cython 可以自动从/转换到 Python 对象的类型。

    不出所料,唯一可以在其签名中包含 C++ 类的函数是 cdef 函数。

    【讨论】:

    • 非常明确的答案。从我查看的其他来源中,这一关键点并不容易理解。谢谢
    猜你喜欢
    • 1970-01-01
    • 2014-06-26
    • 1970-01-01
    • 2013-03-28
    • 2013-06-05
    • 2013-09-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多