Python C API で複数のモジュール/タイプを使用していますか?
-
22-09-2019 - |
質問
2 つの異なる Python 拡張モジュールがあります。それらをAとBと呼びましょう。モジュール A には、モジュール B 内でクラス メソッドの戻り値の型として使用するコンテナーと呼ばれるストレージ クラス型が含まれています。
これをどのように行うべきかについてのドキュメントが見つからないようです。この記事の大まかに従ってモジュール/クラスを作成しましたが、アクセスできるようにすべてのメソッドを静的として宣言しませんでした。 http://nedbatchelder.com/text/whirlext.html
そこで私の質問は、モジュール B のクラス メソッドの PyObject* 戻り値として渡すことができるコンテナのインスタンスを作成するにはどうすればよいでしょうか?コンテナ定義は次のようになります。
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()
を提供します。
これは2つの拡張モジュール間の相互通信を行うための通常の方法は、Python APIを介してそれを行うことであると述べました。あなたが輸入・使用のモジュールA、モジュールBをしたい場合は、あなたが気に型オブジェクトを取得するPyImport_Import()
、モジュールオブジェクトを取得するためにPyObject_GetAttrString()
を呼び出し、PyObject_Call*()
の一つは、それをインスタンス化します。あなたがその型へのポインタを格納したい任意の場所には、あなただけのPyObject *
を取るだろう。