Использование нескольких модулей/типов с API Python C?
-
22-09-2019 - |
Вопрос
У меня есть два разных модуля расширения Python;назовем их А и Б.Модуль A содержит тип класса хранения, называемый контейнером, который я хочу использовать в модуле B в качестве возвращаемого типа метода класса.
Кажется, я не могу найти никакой документации о том, как мне это сделать.Я примерно следовал этой статье при создании модулей/классов, за исключением того, что я не объявил все методы статическими, чтобы они были доступны: http://nedbatchelder.com/text/whirlext.html
Тогда у меня вопрос: как мне создать экземпляр контейнера, который я могу передать обратно как возвращаемое значение PyObject* метода класса в модуле B?Определение контейнера выглядит следующим образом:
typedef struct {
PyObject_HEAD
storageclass* cnt_;
} container;
Я попытался просто сделать следующее в рассматриваемом методе, гдеContainer_init — это метод, который я зарегистрировал как tp_init для класса контейнера:
pycnt::container* retval;
pycnt::container_init(retval, NULL, NULL);
return (PyObject*)retval;
Однако, согласно интерпретатору Python, я возвращаю класс, в котором вызвал метод.(т. е. myclassinstance.mymethod() возвращает myclassinstance).
Очевидно, я поступаю неправильным путем, но понятия не имею, какой путь правильный.Какие-либо предложения?Просто чтобы отрезать тех, кто предложит это: нет, я не заинтересован в использовании SWIG или Boost::Python.Я уже пробовал это, и базовый класс хранения для контейнера тоже не очень хорошо работал (SWIG даже не смог его проанализировать).До сих пор самостоятельное расширение было довольно безболезненным, но я в тупике.
Решение
Ваша проблема в том, что type->tp_init
не делает то, что ты хочешь. type->tp_init
вызывается после выделения объекта для создания экземпляра.Сначала вам нужно будет выделить объект, используя type->tp_new
, затем передайте этот объект в качестве первого аргумента type->tp_init
.Поскольку вы передаете неинициализированный указатель на tp_init
, все ставки сделаны.Однако обычно вы не вызываете ни одну из функций напрямую, а вместо этого вызываете одну из PyObject_Call*()
на самом объекте типа, чтобы создать новый экземпляр.Или предоставить нормальную функцию C для создания нового экземпляра, а-ля PyFoo_New()
.
Тем не менее, обычный способ взаимодействия между двумя модулями расширения — это сделать это через API Python.Если вы хотите, чтобы модуль B импортировал и использовал модуль A, вы вызываете PyImport_Import()
чтобы получить объект модуля, PyObject_GetAttrString()
чтобы получить интересующий вас объект типа и один из PyObject_Call*()
чтобы создать его экземпляр.В любом месте, где вы хотите сохранить указатель на этот тип, вам просто нужно взять PyObject *
.