【发布时间】:2019-09-30 16:12:32
【问题描述】:
我使用 Cython 来包装 C++ 代码。该代码包含一个定义为的函数:
std::vector<ClassOut> analyze(std::vector<ClassIn> inputVec);
ClassIn 和 ClassOut 是扩展类型。在 Python 中,我希望能够使用列表或 numpy 数组调用此函数(尽可能且最明智)。我还希望能够访问和修改扩展类型,所以是这样的:
运行.py
from cythonCode.classIn import PyClassIn
from cythonCode.classOut import PyClassOut
from cythonCode.analyze import PyAnalyze
classIn_list = []
classIn_list.append(PyClassIn())
classIn_list.append(PyClassIn())
classOut_list = PyAnalyze(classIn_list)
print(classOut_list)
包装器 PyClassIn 和 PyClassOut 工作正常。问题只是从一开始就包装了分析功能。我的包装器 PyAnalyze 版本可以在下面找到:
分析.pxd
from libcpp.vector cimport vector
from classOut cimport ClassOut
from classIn cimport ClassIn, PyClassIn
cdef extern from "../cppCode/analyze.h":
vector[ClassOut] analyze(vector[ClassIn])
分析.pyx
def PyAnalyze(vector<PyClassIn> inputVec)
return analyze(inputVec)
analyze.pyx 中肯定有错误。我收到错误消息:
Python object type 'PyClassIn' cannot be used as a template argument
return 语句也必须不正确。 Cython 抱怨:
Cannot convert 'vector[ClassOut]' to Python object
我有这个代码作为https://github.com/zyzzler/cython-vector-minimal-example.git的最小示例
编辑:感谢您的输入,我现在可以包装定义的返回类型但参数还没有。第一条评论中的链接提供了有关正确获取返回类型的重要信息。所以假设我想包装一个定义为的函数:
std::vector<ClassOut> analyze(std::vector<float> inputVec);
一切正常!但是,我必须处理扩展类型 ClassIn 来代替浮动。所以下面是我现在的代码:
分析.pyx
def PyAnalyze(classesIn):
cdef vector[ClassOut] classesOut = analyze(classesIn)
retval = PyClassOutVector()
retval.move_from(move(classesOut))
return retval
以上代码抛出错误:
Cannot convert Python object to 'vector[ClassIn]'
这个错误的原因很清楚。 “classesIn”是 PyClassIn 对象的 Python 列表,但 analyze(...) 将向量 [ClassIn] 作为输入。所以问题是如何从 Python 列表转换为 std::vector 和/或从 PyClassIn 转换为 ClassIn?我也尝试使用右值引用并移动构造函数形式,但它没有用。我也尝试通过这样的函数来做到这一点:
cdef vector[ClassIn] list_to_vec(classInList):
cdef vector[ClassIn] classInVec
for classIn in classInList:
classInVec.push_back(<ClassIn>classIn)
return classInVec
这里的问题是<ClassIn>classIn 语句。它说:
no matching function for call to 'ClassIn::ClassIn(PyObject*&)'
所以我在这里真的很困惑。这怎么可能解决?我使用上面发布的 git 中的最小示例修改了代码。
EDIT2:为下面的 cmets 提供更多信息。我现在有一个 PyClassInVector 的包装器,与 PyClassOutVector 的包装器完全相同,见下文:
cdef class PyClassInVector:
cdef vector[ClassIn] vec
cdef move_from(self, vector[ClassIn]&& move_this):
self.vec = move(move_this)
def __getitem__(self, idx):
return PyClassIn2(self, idx)
def __len__(self):
return self.vec.size()
cdef class PyClassIn2:
cdef ClassIn* thisptr
cdef PyClassInVector vector
def __cinit__(self, PyClassInVector vec, idx):
self.vector = vec
self.thisptr = &vec.vec[idx]
在analyze.pxd我还加了:
cdef extern from "<utility>":
vector[ClassIn]&& move(vector[ClassIn]&&)
现在基于 cmets,在 PyAnalyzefunction 我会做:
def PyAnalyze(classesIn):
# classesIn is a list of PyClassIn objects and needs to be converted to a PyClassInVector
classInVec = PyClassInVector()
cdef vector[ClassOut] classesOut = analyze(classInVec.vec)
retval = PyClassOutVector()
retval.move_from(move(classesOut))
return retval
但是正如代码中的注释所说,我怎样才能将 PyClassIn 对象 (classesIn) 的列表放入 PyClassInVector (classInVec) 中?
EDIT3:想象PyClassOut 装饰有一个可以通过构造函数设置的属性:
cdef class PyClassOut()
def __cinit__(self, number):
self.classOut_c = ClassOut(number)
@property
def number(self):
return self.classOut_c.number
在run.py 我正在做这样的事情:
from cythonCode.classIn import PyClassIn
from cythonCode.classOut import PyClassOut
from cythonCode.analyze import PyAnalyze
classIn_list = []
classIn_list.append(PyClassIn(1))
classIn_list.append(PyClassIn(2))
classOut_list = PyAnalyze(classIn_list)
print(classOut_list[0].number)
print(classOut_list[1].number)
classOut_list 本质上是来自PyAnalyze 函数的retvalue。 retvalue 是一个PyClassOutVector 对象。所以classOut_list[0] 给了我索引0 处的PyClassOut2 对象。但是在这里我无法访问属性number。我还注意到classOut_list[1] 的地址与classOut_list[0] 的地址相同。我不明白这一点。我不完全确定“移动”的作用。此外,我实际上希望再次将 python 列表作为retvalue,理想情况下使用PyClassOut 对象而不是PyClassOut2 对象。那有意义吗?可行吗?
【问题讨论】:
-
基于this answer 的东西可能是合适的 - 它可以让您将向量包装在 Cython 中。
-
非常感谢您的意见!这确实有助于解决我的问题的一部分。我做了一个编辑来解释它。对于另一部分,我仍然感到困惑。
-
classIn 是一个
PyClassIn对象 - 要使用您的方案,您必须告诉 Cython,然后获取它包装的对象:classInVec.push_back(<PyClassIn>classIn.thisptr)(或类似的东西)。您还可以考虑定义一个ClassInVectorPython 包装器 - 如果您将其参数更改为 const 引用,它可以让您在没有副本的情况下传递给analyse。 -
(我已经稍微更新了我对链接问题的回答 - 在重新阅读我的
PyClassIn示例时,如果调整矢量大小会出错。除非你确定那不会发生可能值得查看更新) -
但是您在链接问题中的示例可以应用于 PyClassIn 吗?我通过定义一个包含
move_from(...)的PyClassInVector进行了尝试。但与PyClassOutVector相比,它必须相反。我无法弄清楚如何编码。那么关于你提到的分析参数中的 const 引用,我应该怎么做呢?
标签: python c++ cython cythonize