Я использую Cython для переноса кода C ++. Код содержит функцию, определенную как:
std::vector<ClassOut> analyze(std::vector<ClassIn> inputVec);
ClassIn и ClassOut являются типами расширения. Из Python я хотел бы иметь возможность вызывать эту функцию со списком или массивом (все, что возможно и наиболее разумно). Я также хочу иметь возможность доступа и изменения типов расширений, так что-то вроде этого:
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 может быть найдена ниже:
analysis.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])
analysis.pyx
def PyAnalyze(vector<PyClassIn> inputVec)
return analyze(inputVec)
В анализе наверняка есть ошибки. дарохранительница. Я получаю сообщение об ошибке:
Python object type 'PyClassIn' cannot be used as a template argument
Оператор возврата также должен быть неверным. 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 вместо float. Ниже приведен код, который у меня есть:
analy.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" - это список Python объектов PyClassIn, но анализ (...) принимает вектор [ClassIn] в качестве входных данных. Итак, вопрос в том, как преобразовать список Python в std :: vector и / или из PyClassIn в ClassIn? Я также пытался использовать ссылку на rvalue и формализм перемещения конструктора, но это не сработало. Я также пытался сделать это с помощью такой функции:
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
- это, по сути, retvalue
из функции PyAnalyze
. Retvalue является PyClassOutVector
объектом. Так что classOut_list[0]
дает мне объект PyClassOut2
с индексом 0. Но здесь у меня нет доступа к атрибуту number
. Также я замечаю, что адрес classOut_list[1]
совпадает с адресом classOut_list[0]
. Я не понимаю этого. Я не совсем уверен, что делает «движение». Кроме того, я на самом деле хочу снова иметь список Python в виде retvalue
, в идеале с PyClassOut
объектами вместо PyClassOut2
объектов. Имеет ли это смысл? И возможно ли это?