使用 Python 包捆绑 Cython 模块
Bundle Cython module with a Python package
我正在用 Cython 包装一个 C++ 库,我想将其作为 Python 包分发。我一直在使用 this tutorial 作为指导。
事情是这样安排的。
.
├── inc
│ └── Rectangle.h
├── rect
│ ├── __init__.py
│ └── wrapper.pyx
├── setup.py
└── src
└── Rectangle.cpp
我已将这些文件的内容粘贴在 post 的底部,以及此 GitHub repo。
我用 python setup.py install
编译和安装没有问题,我可以从解释器 import rect
毫无问题。但它似乎是一个空的 class:我无法使用以下任何一项创建 Rectangle
对象。
- Rectangle
- rect.Rectangle
- wrapper.Rectangle
- rect.wrapper.Rectangle
我做错了什么?
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();
void getSize(int* width, int* height);
void move(int dx, int dy);
};
}
Rectangle.cpp
的内容。
#include "Rectangle.h"
namespace shapes {
Rectangle::Rectangle() { }
Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) {
x0 = X0;
y0 = Y0;
x1 = X1;
y1 = Y1;
}
Rectangle::~Rectangle() { }
int Rectangle::getArea() {
return (x1 - x0) * (y1 - y0);
}
void Rectangle::getSize(int *width, int *height) {
(*width) = x1 - x0;
(*height) = y1 - y0;
}
void Rectangle::move(int dx, int dy) {
x0 += dx;
y0 += dy;
x1 += dx;
y1 += dy;
}
}
Cython 包装器代码 wrapper.pyx
。
# distutils: language = c++
# distutils: sources = src/Rectangle.cpp
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()
void getSize(int* width, int* height)
void move(int, int)
cdef class PyRectangle:
cdef Rectangle c_rect # hold a C++ instance which we're wrapping
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()
def get_size(self):
cdef int width, height
self.c_rect.getSize(&width, &height)
return width, height
def move(self, dx, dy):
self.c_rect.move(dx, dy)
我为这个文件组织改编的setup.py
脚本。
from distutils.core import setup, Extension
from Cython.Build import cythonize
setup(
name='rect',
packages=['rect'],
ext_modules=cythonize(Extension(
'Rectangle',
sources=['rect/wrapper.pyx', 'src/Rectangle.cpp'],
include_dirs=['inc/'],
language='c++',
extra_compile_args=['--std=c++11'],
extra_link_args=['--std=c++11']
)),
)
您的导入语句作用于 __init__.py
,这可能是空的
但是您的安装脚本应该创建一个 .so
文件。因此,您必须 运行 它不是 install
,而是 build_ext
(和 --inplace
),它为您提供了可以随后导入的 .so
文件。但是要小心你在 setup.py
中的名字,你最终会在调用 import rect
时导入几个可能的项目。避免扩展名与包名相同(或之后相应地修改 __init__.py
文件)
在这种情况下,问题出在 .pyx
文件和扩展名不相同。当我将 wrapper.pyx
重命名为 Rectangle
并重新安装时,我能够 运行 在 Python 解释器中执行以下操作。
>>> import Rectangle
>>> r = Rectangle.PyRectangle(0, 0, 1, 1)
>>> r.get_area()
1
>>>
我正在用 Cython 包装一个 C++ 库,我想将其作为 Python 包分发。我一直在使用 this tutorial 作为指导。
事情是这样安排的。
.
├── inc
│ └── Rectangle.h
├── rect
│ ├── __init__.py
│ └── wrapper.pyx
├── setup.py
└── src
└── Rectangle.cpp
我已将这些文件的内容粘贴在 post 的底部,以及此 GitHub repo。
我用 python setup.py install
编译和安装没有问题,我可以从解释器 import rect
毫无问题。但它似乎是一个空的 class:我无法使用以下任何一项创建 Rectangle
对象。
- Rectangle
- rect.Rectangle
- wrapper.Rectangle
- rect.wrapper.Rectangle
我做错了什么?
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();
void getSize(int* width, int* height);
void move(int dx, int dy);
};
}
Rectangle.cpp
的内容。
#include "Rectangle.h"
namespace shapes {
Rectangle::Rectangle() { }
Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) {
x0 = X0;
y0 = Y0;
x1 = X1;
y1 = Y1;
}
Rectangle::~Rectangle() { }
int Rectangle::getArea() {
return (x1 - x0) * (y1 - y0);
}
void Rectangle::getSize(int *width, int *height) {
(*width) = x1 - x0;
(*height) = y1 - y0;
}
void Rectangle::move(int dx, int dy) {
x0 += dx;
y0 += dy;
x1 += dx;
y1 += dy;
}
}
Cython 包装器代码 wrapper.pyx
。
# distutils: language = c++
# distutils: sources = src/Rectangle.cpp
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()
void getSize(int* width, int* height)
void move(int, int)
cdef class PyRectangle:
cdef Rectangle c_rect # hold a C++ instance which we're wrapping
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()
def get_size(self):
cdef int width, height
self.c_rect.getSize(&width, &height)
return width, height
def move(self, dx, dy):
self.c_rect.move(dx, dy)
我为这个文件组织改编的setup.py
脚本。
from distutils.core import setup, Extension
from Cython.Build import cythonize
setup(
name='rect',
packages=['rect'],
ext_modules=cythonize(Extension(
'Rectangle',
sources=['rect/wrapper.pyx', 'src/Rectangle.cpp'],
include_dirs=['inc/'],
language='c++',
extra_compile_args=['--std=c++11'],
extra_link_args=['--std=c++11']
)),
)
您的导入语句作用于 __init__.py
,这可能是空的
但是您的安装脚本应该创建一个 .so
文件。因此,您必须 运行 它不是 install
,而是 build_ext
(和 --inplace
),它为您提供了可以随后导入的 .so
文件。但是要小心你在 setup.py
中的名字,你最终会在调用 import rect
时导入几个可能的项目。避免扩展名与包名相同(或之后相应地修改 __init__.py
文件)
在这种情况下,问题出在 .pyx
文件和扩展名不相同。当我将 wrapper.pyx
重命名为 Rectangle
并重新安装时,我能够 运行 在 Python 解释器中执行以下操作。
>>> import Rectangle
>>> r = Rectangle.PyRectangle(0, 0, 1, 1)
>>> r.get_area()
1
>>>