如何将 Python 对象转换为 Cython 扩展类型的 std::vector 并返回?
How to convert Python object to a std::vector of Cython extension type and back?
我使用 Cython 来包装 C++ 代码。该代码包含定义为的函数:
std::vector<ClassOut> analyze(std::vector<ClassIn> inputVec);
ClassIn 和 ClassOut 是扩展类型。从 Python 我希望能够使用列表或 numpy 数组调用此函数(任何可能且最明智的)。我还希望能够访问和修改扩展类型,所以像这样:
run.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 版本可以在下面找到:
analyze.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])
analyze.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
中有此代码作为最小示例
编辑:感谢您的输入,我现在可以包装定义的 return 类型,但还不能包装参数。第一条评论中的 link 提供了有关正确设置 return 类型的重要信息。所以假设我想包装一个定义为的函数:
std::vector<ClassOut> analyze(std::vector<float> inputVec);
一切正常!但是,我必须处理扩展类型 ClassIn 来代替浮点数。下面是我现在的代码:
analyze.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(...) 将 vector[ClassIn] 作为输入。所以问题是如何从Python列表转换为std::vectorand/or从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:为下面的评论提供更多信息。我现在有一个 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]&&)
现在根据评论,在 PyAnalyze
函数中我会做:
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
。重设值是一个 PyClassOutVector
对象。所以 classOut_list[0]
在索引 0 处给了我 PyClassOut2
对象。但是在这里我无权访问属性 number
。我还注意到 classOut_list[1]
的地址与 classOut_list[0]
的地址相同。我不明白这一点。我不完全确定 'move' 是做什么的。此外,我实际上想要再次使用 python 列表作为 retvalue
,理想情况下使用 PyClassOut
对象而不是 PyClassOut2
对象。那有意义吗?可行吗?
在评论中,我试图推荐一种涉及包装 C++ 向量的解决方案。我更喜欢这种方法,因为它避免了多次复制内存,但我认为它会造成更多的混乱,你宁愿只使用 Python 列表。对不起。
要使用 Python 列表,您只需在 PyAnalyze
中复制输入和输出。您必须手动执行 - 不存在自动转换。您还必须了解包装的 类 和底层 C++ 类 之间的区别。您只能将 C++ 类 发送到 C++,而不是包装的。
处理输入很简单:
def PyAnalyze(classesIn):
# classesIn is a list of PyClassIn objects and needs to be converted to a PyClassInVector
cdef vector[ClassIn] vecIn
cdef vector[ClassOut] vecOut
cdef PyClassIn a
for a in classesIn:
# need to type a to access its C attributes
# Cython should check that a is of the correct type
vecIn.push_back(a.classIn_c)
vecOut = analyze(vecIn)
将数据返回到包装为 PyClassOut 的 Cython 有点困难,因为您不能将 C++ 类型发送到 Cython 构造函数(构造函数的所有参数必须是 Python 类型)。只需构建一个空的 PyClassOut
然后将新数据复制到其中。同样,逐个元素
处理你的向量
def PyAnalyze(classesIn):
cdef PyClassOut out_val
# ... use code above ...
out_list = []
for i in range(vecOut.size()):
out_val = PyClassOut()
out_val.classOut_c = vecOut[i]
out_list.append(out_val)
return out_list
我使用 Cython 来包装 C++ 代码。该代码包含定义为的函数:
std::vector<ClassOut> analyze(std::vector<ClassIn> inputVec);
ClassIn 和 ClassOut 是扩展类型。从 Python 我希望能够使用列表或 numpy 数组调用此函数(任何可能且最明智的)。我还希望能够访问和修改扩展类型,所以像这样:
run.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 版本可以在下面找到:
analyze.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])
analyze.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
中有此代码作为最小示例编辑:感谢您的输入,我现在可以包装定义的 return 类型,但还不能包装参数。第一条评论中的 link 提供了有关正确设置 return 类型的重要信息。所以假设我想包装一个定义为的函数:
std::vector<ClassOut> analyze(std::vector<float> inputVec);
一切正常!但是,我必须处理扩展类型 ClassIn 来代替浮点数。下面是我现在的代码:
analyze.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(...) 将 vector[ClassIn] 作为输入。所以问题是如何从Python列表转换为std::vectorand/or从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:为下面的评论提供更多信息。我现在有一个 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]&&)
现在根据评论,在 PyAnalyze
函数中我会做:
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
。重设值是一个 PyClassOutVector
对象。所以 classOut_list[0]
在索引 0 处给了我 PyClassOut2
对象。但是在这里我无权访问属性 number
。我还注意到 classOut_list[1]
的地址与 classOut_list[0]
的地址相同。我不明白这一点。我不完全确定 'move' 是做什么的。此外,我实际上想要再次使用 python 列表作为 retvalue
,理想情况下使用 PyClassOut
对象而不是 PyClassOut2
对象。那有意义吗?可行吗?
在评论中,我试图推荐一种涉及包装 C++ 向量的解决方案。我更喜欢这种方法,因为它避免了多次复制内存,但我认为它会造成更多的混乱,你宁愿只使用 Python 列表。对不起。
要使用 Python 列表,您只需在 PyAnalyze
中复制输入和输出。您必须手动执行 - 不存在自动转换。您还必须了解包装的 类 和底层 C++ 类 之间的区别。您只能将 C++ 类 发送到 C++,而不是包装的。
处理输入很简单:
def PyAnalyze(classesIn):
# classesIn is a list of PyClassIn objects and needs to be converted to a PyClassInVector
cdef vector[ClassIn] vecIn
cdef vector[ClassOut] vecOut
cdef PyClassIn a
for a in classesIn:
# need to type a to access its C attributes
# Cython should check that a is of the correct type
vecIn.push_back(a.classIn_c)
vecOut = analyze(vecIn)
将数据返回到包装为 PyClassOut 的 Cython 有点困难,因为您不能将 C++ 类型发送到 Cython 构造函数(构造函数的所有参数必须是 Python 类型)。只需构建一个空的 PyClassOut
然后将新数据复制到其中。同样,逐个元素
def PyAnalyze(classesIn):
cdef PyClassOut out_val
# ... use code above ...
out_list = []
for i in range(vecOut.size()):
out_val = PyClassOut()
out_val.classOut_c = vecOut[i]
out_list.append(out_val)
return out_list