Я использую 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)
В analysis.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
EDIT: благодаря вашему вкладу я сейчас нахожусь в точке, где возвращаемый тип определения может быть обернут, но аргумент еще нет. Ссылка в первом комментарии предоставила отличную информацию о правильном типе возвращаемого значения. Итак, если я хочу обернуть функцию, определенную как:
std::vector<ClassOut> analyze(std::vector<float> inputVec);
все работает нормально! Однако мне приходится иметь дело с ClassIn типа расширения вместо float. Итак, ниже приведен код, который у меня есть сейчас:
анализировать.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
function я бы сделал:
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
. Возвращаемое значение — это объект PyClassOutVector
. Итак, classOut_list[0]
дает мне объект PyClassOut2
с индексом 0. Но здесь у меня нет доступа к атрибуту number
. Также я заметил, что адрес classOut_list[1]
совпадает с адресом classOut_list[0]
. Я этого не понимаю. Я не совсем уверен, что делает «перемещение». Кроме того, я действительно хочу снова иметь список python как retvalue
, в идеале с объектами PyClassOut
вместо объектов PyClassOut2
. Имеет ли это смысл? И возможно ли это?
PyClassIn
- чтобы использовать вашу схему, вам нужно сообщить Cython об этом, а затем получить объект, который он обертывает:classInVec.push_back(<PyClassIn>classIn.thisptr)
(или что-то подобное). Вы также можете рассмотреть возможность определения оболочкиClassInVector
Python, которая позволит вам перейти кanalyse
без копии, если вы измените его аргумент на константную ссылку. - person DavidW   schedule 01.10.2019PyClassIn
происходит ошибка, если размер вектора изменяется. Если вы не уверены, что этого не произойдет, возможно, стоит посмотреть на обновление) - person DavidW   schedule 01.10.2019PyClassInVector
с включеннымmove_from(...)
. Но должно быть наоборот по сравнению сPyClassOutVector
. Я не мог понять, как это закодировать. Итак, что касается ссылки const в аргументе анализа, о котором вы упомянули, как мне это сделать? - person zyzzler   schedule 01.10.2019classInVec.thisptr
вanalyze
(и объявите типclassInVec
какPyClassInVector
) - person DavidW   schedule 01.10.2019list_to_vec
, мне нужно было бы расширитьPyClassInVector
специальным методом__setitem__
. Иначе я не понимаю, как можно будет перенести список объектов PyClassIn в вектор объектов ClassIn, а вы? - person zyzzler   schedule 01.10.2019PyClassInVector
, а неlist
. Тем не менее, похоже, что вам может быть лучше просто работать со списком, как вы пытались сделать в редактировании 1 - этот код выглядел довольно близко к работе. - person DavidW   schedule 01.10.2019list_to_vec
теперь работает. Но у меня проблемы сPyClassOutVector
. Я просмотрел изменения, которые вы внесли в свой исходный пост в связанном вопросе, чтобы посмотреть, поможет ли это. Я заметил одну вещь при реализации ваших изменений: в__cinit__
должно бытьself.idx = idx
вместоself.thisptr...
. Тем не менее, моя проблема остается. Я описываю это в EDIT3. - person zyzzler   schedule 02.10.2019PyClassOutVector
иPyClassInVector
. Я также предлагаю вам забыть оmove
и просто копировать элементы поэлементно вPyAnalyze
. Это был подход, который я предпочитаю (в основном, чтобы избежать копирования), но я думаю, что это немного сбивает с толку и не помогает вам. - person DavidW   schedule 02.10.2019