Cython示例具有多个类

时间:2018-09-10 17:23:19

标签: python c++ cython

我请求您的帮助,因为我正在尝试运行一个cython示例,它比许多教程(例如this guide)中可能包含一个类的cython示例要复杂得多。我还没有找到任何“更高级的”教程,所以我希望这个问题对试图深入学习它的人也有用。

我将在此处写下我采取的步骤,希望有人能告诉我我的错误在哪里。

我有一个 Rectangle c ++类(我在这里只是放了.h文件以使其更短):

#ifndef RECTANGLE_H
#define RECTANGLE_H
namespace shapes { 
    class Rectangle {
        public:
            int x0, y0, x1, y1;
            Rectangle();
            Rectangle(int x0, int y0, int x1, int y1);
            ~Rectangle();
            int getArea();
    };
}
#endif

Group2 类。一个非常简单的示例类,其构造函数将2个矩形作为输入:

#ifndef GROUP4_H
#define GROUP4_H
#include "Rectangle.h"
namespace shapes{
    class Group2 {
    public:
        Rectangle rect0, rect1, rect2, rect3 ;
        Group2();
        Group2(Rectangle rect0, Rectangle rect1);
        ~Group2();
        void getAreas(int *area0, int *area1);
    };
}
#endif

然后我创建一个 grp2.pyx 文件,同时定义 Rectangle Group2 类:

#RECTANGLE
cdef extern from "Rectangle.h" namespace "shapes":
    cdef cppclass Rectangle:
        Rectangle() except +
        Rectangle(int, int, int, int) except +
        int x0, y0, x1, y1
        int getArea()

cdef class PyRectangle:
    cdef Rectangle c_rect
    def __cinit__(self, int x0, int y0, int x1, int y1):
        self.c_rect = Rectangle(x0, y0, x1, y1)
    def get_area(self):
        return self.c_rect.getArea()

# GROUP2
cdef extern from "Group2.h" namespace "shapes":
    cdef cppclass Group2:
        Group2() except +
        Group2(Rectangle rect0, Rectangle rect1) except +
        void getAreas(int *area0, int *area1)
cdef class PyGroup2:
    cdef Group2 c_group2
    def __cinit__(self, Rectangle rect0, Rectangle rect1):
        self.c_group2 = Group2(rect0, rect1)
    def get_areas(self):
        cdef int area0, area1
        self.c_group2.getAreas(&area0, &area1)
        return area0, area1

然后我使用命令行在静态c ++库中编译这两个类:

gcc -c -fPIC Group2.cpp Rectangle.cpp

ar rcs libexample.a Group2.o Rectangle.o

最后,我创建从命令行调用的cython 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 and
           libraries=["example"],
           library_dirs=["lib"],
           include_dirs=["lib"],
                                                  # additional C++ source files
           language="c++",                        # generate and compile C++ code
      )))

这时我在 PyGroup2 _cinint _ 中出现错误:

  

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

我想我的pyx文件中有一些错误,但是我不能说出什么,因为我在那里定义了python的 Rectangle

1 个答案:

答案 0 :(得分:1)

在将矩形传递给C ++函数时,应在PyRectangle函数的签名中使用def,并在PyRectangle.c_rect的签名中使用。{p>

这意味着您的代码应为:

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

请继续阅读,以详细了解原因。


传递给def-函数的所有参数都是Python对象(即Cython-parlance中的object类型),之后所有这些函数都将从纯Python(仅知道Python对象)进行调用

例如,您可以添加一些语法糖并在def函数的签名中使用“ late-binding”代替

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_AsLong,{{3} }。 Cython还处理错误检查,因此您不应该手动进行这些转换。

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

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

Cython负责从objectPyRectangle的转换之后,必须通过利用PyRectangle手动完成从Rectanglec_rect的最后一步-指针:

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

cpdef函数的规则相似,因为可以从纯Python调用它们。 PyFloat_AsDouble仅适用于Cython可以自动覆盖Python对象的类型。

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