Usando vários módulos/tipos com API Python C?
-
22-09-2019 - |
Pergunta
Eu tenho dois módulos de extensão Python diferentes; Vamos chamá -los de A e B. O módulo A contém um tipo de classe de armazenamento chamado contêiner que eu desejo usar no módulo B como o tipo de retorno de um método de classe.
Não consigo encontrar nenhuma documentação sobre como devo fazer isso. Eu segui aproximadamente este artigo para criar os módulos/classes, exceto que não declarei todos os métodos como estáticos, para que eles estejam acessíveis: http://nedbatchelder.com/text/whirlext.html
Minha pergunta é então, como faço para criar uma instância de contêiner que eu possa passar de volta como o valor de retorno Pyobject* de um método de classe no módulo B? A definição de contêiner é assim:
typedef struct {
PyObject_HEAD
storageclass* cnt_;
} container;
Tentei simplesmente fazer o seguinte no método em questão, onde o container_init é o método que eu registrei como tp_init para a classe de contêiner:
pycnt::container* retval;
pycnt::container_init(retval, NULL, NULL);
return (PyObject*)retval;
No entanto, de acordo com o intérprete Python, estou recebendo a aula em que chamei o método. (ou seja, myclassinstance.mymethod () está retornando myclassinstance).
Obviamente, estou fazendo isso da maneira errada, mas não tenho idéia do caminho certo. Alguma sugestão? Só para cortar alguém que vai sugerir - não, não estou interessado em usar Swig ou Boost :: Python. Eu já tentei isso e a classe de armazenamento subjacente para o contêiner não jogava bem com a mesma coisa (o SWIG nem conseguia analisá -lo). Até agora, fazer as extensões eu mesmo tem sido bastante indolor, mas estou perplexo com isso.
Solução
Seu problema é que type->tp_init
não faz o que você quer. type->tp_init
é chamado depois que um objeto é alocado para fazer a instanciação. Você primeiro teria que alocar o objeto usando type->tp_new
, depois passe esse objeto como o primeiro argumento para type->tp_init
. Desde que você passa um ponteiro não inicializado para tp_init
, todas as apostas estão encerradas. Você normalmente não chama nenhuma função diretamente, e, em vez disso, chama um dos PyObject_Call*()
no próprio objeto de tipo para criar uma nova instância. Ou forneça uma função C normal para criar uma nova instância, uma la PyFoo_New()
.
Dito isto, a maneira usual de fazer intercomunicação entre dois módulos de extensão é fazê -lo através da API Python. Se você deseja que o Módulo B importe e use o módulo A, você liga PyImport_Import()
Para obter o objeto do módulo, PyObject_GetAttrString()
para obter o objeto de tipo que você se importa e um dos PyObject_Call*()
para instanciar isso. Qualquer lugar que você queira armazenar um ponteiro para esse tipo, você apenas aceitaria um PyObject *
.