End*_*age 20 c++ python cython python-2.7
我有一个C++类.它由一个.ccp文件和一个.h文件组成.它编译(我可以编写一个在c ++中成功使用它的main方法).如何使用Cython包装此类以使其在Python中可用?
我已经阅读了文档而没有关注.他们谈论生成cpp文件.当我试图关注文档时,我已经存在的cpp被吹走了......
我打算把什么放在pyx文件中?我被告知课程定义,但有多少?只是公共方法?
我需要一个.pxd文件吗?我不明白何时需要此文件.
我已经尝试在#python IRC频道中提出这些问题并且无法得到答案.
Nik*_*s R 16
即使Cython通常与C一起使用,它也可以生成C++代码.编译时,添加--cplus标志.
现在,为类创建包装器很简单,与包装结构没有多大区别.它主要不同于声明extern,但这根本没有太大区别.
假设你有一个类MyCppClass在mycppclass.h.
cdef extern from "mycppclass.h":
cppclass MyCppClass:
int some_var
MyCppClass(int, char*)
void doStuff(void*)
char* getStuff(int)
cdef class MyClass:
# the public-modifier will make the attribute public for cython,
# not for python. Maybe you need to access the internal C++ object from
# outside of the class. If not, you better declare it as private by just
# leaving out the `private` modifier.
# ---- EDIT ------
# Sorry, this statement is wrong. The `private` modifier would make it available to Python,
# so the following line would cause an error es the Pointer to MyCppClass
# couldn't be converted to a Python object.
#>> cdef public MyCppClass* cobj
# correct is:
cdef MyCppClass* obj
def __init__(self, int some_var, char* some_string):
self.cobj = new MyCppClass(some_var, some_string)
if self.cobj == NULL:
raise MemoryError('Not enough memory.')
def __del__(self):
del self.cobj
property some_var:
def __get__(self):
return self.cobj.some_var
def __set__(self, int var):
self.cobj.some_var = var
Run Code Online (Sandbox Code Playgroud)
请注意,new关键字只有当可用的--cplus标志设置,否则使用malloc从<stdlib.h>由外扩了.
另请注意,您无需取消引用指针(->)即可调用该方法.Cython跟踪对象的类型并应用适合的类型.
.pxd文件用于从实现中分离声明,或避免命名空间冲突.想象一下,您想要像C++类一样命名Python包装器.只需在.px文件中输入extern声明和cimport.pyx中的pxd文件即可.
cimport my_pxd
cdef my_pxd.SomeExternedType obj
Run Code Online (Sandbox Code Playgroud)
请注意,您无法在.pxd文件中编写实现.
因此经过大量的探索,试验和错误,尖叫和撕裂我的头发,我终于得到了这个工作.首先,我不得不将我的C++重新编写成C语言,这对我来说实际上只涉及将所有std::string变量转换为char*跟踪一些长度.
完成后我得到了.h和.c文件.我想从Python中提供的C代码中创建一个函数.事实证明,Cython可以将你的C文件编译成你的扩展并一次性链接任何库,所以从我的setup.py开始,它看起来像这样:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext_modules=[
Extension("myext",
["myext.pyx", "../stuff.c"],
libraries=["ssl", "crypto"]
)
]
setup(
name = "myext",
cmdclass = {"build_ext": build_ext},
ext_modules = ext_modules
)
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,Extension的第二个参数只列出了需要编译的所有文件,据我所知,Cython根据文件扩展名计算出如何编译它们.库数组告诉Cython编译器需要链接什么(在这种情况下,我正在包装一些我似乎无法通过现有Python库直接模仿的加密内容).
要在.pyx文件中实际使用我的C函数,可以在.pxd中编写一个小包装器.我的myext.pxd看起来如下:
cdef extern from "../stuff.h":
char* myfunc(char* arg1, char* arg2, char* arg3)
Run Code Online (Sandbox Code Playgroud)
然后在.pyx中使用cimport声明来导入此函数,然后可以使用该函数,就好像它是任何其他Python函数一样:
cimport myext
def my_python_func(arg1, arg2, arg3):
result = myext.myfunc(arg1, arg2, arg3)
return result
Run Code Online (Sandbox Code Playgroud)
当你构建它时(至少在Mac上),你得到一个.so,你可以在python中导入并运行.pyx中的函数.可能有更好,更正确的方法让这一切全部有效,但这来自经验,这是我第一次遇到的问题.我对可能出错的指针非常感兴趣.
更新:
在进一步使用Cython之后,我发现将它与C++集成起来非常简单,一旦你知道你在做什么.使C++ string可用就像from libcpp.string cimport string你的pyx/pyd 一样简单.声明C++类同样容易:
cdef extern from "MyCPPClass.h":
cdef cppclass MyCPPClass:
int foo;
string bar;
Run Code Online (Sandbox Code Playgroud)
当然,您必须以Pythonic格式重新声明类的.h定义,但这对于访问已编写的C++函数来说是一个很小的代价.