拡張と埋め込み FAQ — Python 3.5.3 ドキュメント
Contents
- 拡張と埋め込み FAQ
- C で独自の関数を作ることはできますか?
- C++ で独自の関数を作ることはできますか?
- C を書くのは大変です。他の方法はありませんか?
- C から任意の Python 文を実行するにはどうしますか?
- C から任意の Python 式を評価するにはどうしますか?
- Python オブジェクトから C の値を取り出すにはどうしますか?
- Py_BuildValue() で任意長のタプルを作るにはどうしますか?
- C からオブジェクトのメソッドを呼び出すにはどうしますか?
- PyErr_Print() (その他 stdout/stderr に印字するもの) からの出力を受け取るにはどうしますか?
- C から Python で書かれたモジュールにアクセスするにはどうしますか?
- Python から C++ へインタフェースするにはどうしますか?
- セットアップファイルでモジュールを追加しようとしたらメイクに失敗しました。なぜですか?
- 拡張をデバッグするにはどうしますか?
- Linux システムで Python モジュールをコンパイルしたいのですが、見つからないファイルがあります。なぜですか?
- “不完全 (incomplete) な入力” を “不適切 (invalid) な入力” から区別するにはどうしますか?
- 未定義の g++ シンボル __builtin_new や __pure_virtual を見つけるにはどうしますか?
- メソッドのいくつかは C で、その他は Python で実装されたオブジェクトクラスを (継承などで) 作ることはできますか?
C で独自の関数を作ることはできますか?¶
はい。関数、変数、例外、そして新しいタイプまで含んだビルトインモジュールを C で作れます。これはドキュメント Python インタプリタの拡張と埋め込み で説明されています。
ほとんどの中級から上級の Python 本もこの話題を扱っています。
C++ で独自の関数を作ることはできますか?¶
はい。C++ 内にある C 互換機能を使ってできます。 extern "C" { ... } で Python のインクルードファイルを囲み、 extern "C" を Python インタプリタから呼ぶ各関数の前に置いてください。グローバルや静的な C++ オブジェクトの構造体を持つものは良くないでしょう。
C を書くのは大変です。他の方法はありませんか?¶
独自の C 拡張を書くための別のやり方は、目的によっていくつかあります。
Cython とその親戚 Pyrex は、わずかに変形した Python を受け取り、対応する C コードを生成します。Cython や Pyrex を使えば Python の C API を習得することなく拡張を書けます。
今のところ Python 拡張が存在しないような C や C++ ライブラリへのインタフェースが必要な場合、SWIG のようなツールで、そのライブラリのデータ型のラッピングを図れます。 SIP、CXX、Boost、Weave でも C++ ライブラリをラッピングできます。
C からオブジェクトのメソッドを呼び出すにはどうしますか?¶
PyObject_CallMethod() 関数でオブジェクトの任意のメソッドを呼び出せます。パラメタは、オブジェクト、呼び出すメソッドの名前、 Py_BuildValue() で使われるようなフォーマット文字列、そして引数です:
PyObject * PyObject_CallMethod(PyObject *object, const char *method_name, const char *arg_format, ...);
これはメソッドを持ついかなるオブジェクトにも有効で、組み込みかユーザ定義かは関係ありません。返り値に対して Py_DECREF() する必要があることもあります。
例えば、あるファイルオブジェクトの “seek” メソッドを 10, 0 を引数として呼ぶとき (ファイルオブジェクトのポインタを “f” とします):
res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0); if (res == NULL) { ... an exception occurred ... } else { Py_DECREF(res); }
なお、 PyObject_CallObject() の引数リストには 常に タプルが必要です。関数を引数なしで呼び出すには、フォーマットに “()” を渡し、関数を一つの引数で呼び出すには、関数を括弧でくくって例えば “(i)” としてください。
PyErr_Print() (その他 stdout/stderr に印字するもの) からの出力を受け取るにはどうしますか?¶
Python コード内で、 write() メソッドをサポートするオブジェクトを定義してください。そのオブジェクトを sys.stdout と sys.stderr に代入してください。print_error を呼び出すか、単に標準のトレースバック機構を作動させてください。そうすれば、出力は write() が送る任意の所に行きます。
最も簡単な方法は、 io.StringIO クラスを使うことです:
>>> import io, sys >>> sys.stdout = io.StringIO() >>> print('foo') >>> print('hello world!') >>> sys.stderr.write(sys.stdout.getvalue()) foo hello world!
これと同じことをするカスタムオブジェクトは次のようになるでしょう:
>>> import io, sys >>> class StdoutCatcher(io.TextIOBase): ... def __init__(self): ... self.data = [] ... def write(self, stuff): ... self.data.append(stuff) ... >>> import sys >>> sys.stdout = StdoutCatcher() >>> print('foo') >>> print('hello world!') >>> sys.stderr.write(''.join(sys.stdout.data)) foo hello world!
C から Python で書かれたモジュールにアクセスするにはどうしますか?¶
以下のようにモジュールオブジェクトへのポインタを得られます:
module = PyImport_ImportModule("<modulename>");
そのモジュールがまだインポートされていない (つまり、まだ sys.modules に現れていない) なら、これはモジュールを初期化します。そうでなければ、単純に sys.modules["<modulename>"] の値を返します。なお、これはモジュールをいかなる名前空間にも代入しません。これはモジュールが初期化されて ‘sys.modules に保管されていることを保証するだけです。
これで、モジュールの属性 (つまり、モジュールで定義された任意の名前) に以下のようにアクセスできるようになります:
attr = PyObject_GetAttrString(module, "<attrname>");
PyObject_SetAttrString() を呼んでモジュールの変数に代入することもできます。
Python から C++ へインタフェースするにはどうしますか?¶
やりたいことに応じて、いろいろな方法があります。手動でやるなら、 “拡張と埋め込み” ドキュメント を読むことから始めてください。なお、Python ランタイムシステムにとっては、 C と C++ はあまり変わりません。だから、C 構造体 (ポインタ)型に基づいて新しい Python の型を構築する方針は C++ オブジェクトに対しても有効です。
C++ ライブラリに関しては、 C を書くのは大変です。他の方法はありませんか? を参照してください。
セットアップファイルでモジュールを追加しようとしたらメイクに失敗しました。なぜですか?¶
セットアップは改行で終わらなければならなくて、改行がないと、ビルド工程は失敗します。(これを直すには、ある種の醜いシェルスクリプトハックが必要ですが、このバグは小さいものですから努力に見合う価値はないでしょう。)
拡張をデバッグするにはどうしますか?¶
動的にロードされた拡張に GDB を使うとき、拡張がロードされるまでブレークポイントを設定してはいけません。
.gdbinit ファイルに(または対話的に)、このコマンドを加えてください:
br _PyImport_LoadDynamicModule
そして、GDB を起動するときに:
$ gdb /local/bin/python gdb) run myscript.py gdb) continue # repeat until your extension is loaded gdb) finish # so that your extension is loaded gdb) br myfunction.c:50 gdb) continue
Linux システムで Python モジュールをコンパイルしたいのですが、見つからないファイルがあります。なぜですか?¶
Python の多くのパッケージバージョンには、Python 拡張をコンパイルするのに必要な様々なファイルを含む /usr/lib/python2.x/config/ ディレクトリが含まれていません。
Red Hat では、Python RPM をインストールして必要なファイルを得てください。
Debian では、 apt-get install python-dev を実行してください。
“不完全 (incomplete) な入力” を “不適切 (invalid) な入力” から区別するにはどうしますか?¶
Python インタラクティブインタプリタでは、入力が不完全なとき (例えば、 “if” 文の始まりをタイプした時や、カッコや三重文字列引用符を閉じていない時など) には継続プロンプトを与えられますが、入力が不適切であるときには即座に構文エラーメッセージが与えられます。このようなふるまいを模倣したいことがあります。
Python では構文解析器のふるまいに十分に近い codeop モジュールが使えます。例えば IDLE がこれを使っています。
これを C で行う最も簡単な方法は、 PyRun_InteractiveLoop() を (必要ならば別のスレッドで) 呼び出し、Python インタプリタにあなたの入力を扱わせることです。独自の入力関数を指定するのに PyOS_ReadlineFunctionPointer() を設定することもできます。詳しいヒントは、 Modules/readline.c や Parser/myreadline.c を参照してください。
しかし、組み込みの Python インタプリタを他のアプリケーションと同じスレッドで実行することが必要で、 PyRun_InteractiveLoop() でユーザの入力を待っている間止められないこともあります。このような場合の解決策の一つは、 PyParser_ParseString() を呼んで e.error と E_EOF が等しいこと、つまり入力が不完全であることを確かめることです。これは、Alex Farber のコードを参考にした、コード片の例です:
#include <Python.h> #include <node.h> #include <errcode.h> #include <grammar.h> #include <parsetok.h> #include <compile.h> int testcomplete(char *code) /* code should end in \n */ /* return -1 for error, 0 for incomplete, 1 for complete */ { node *n; perrdetail e; n = PyParser_ParseString(code, &_PyParser_Grammar, Py_file_input, &e); if (n == NULL) { if (e.error == E_EOF) return 0; return -1; } PyNode_Free(n); return 1; }
別の解決策は、受け取られた文字列を Py_CompileString() でコンパイルすることを試みることです。エラー無くコンパイルされたら、返されたコードオブジェクトを PyEval_EvalCode() を呼んで実行することを試みてください。そうでなければ、入力を後のために保存してください。コンパイルが失敗したなら、それがエラーなのか入力の続きが求められているだけなのか調べてください。そのためには、例外タプルからメッセージ文字列を展開し、それを文字列 “unexpected EOF while parsing” と比較します。ここに GNU readline library を使った完全な例があります (readline() を読んでいる間は SIGINT を無視したいかもしれません):
#include <stdio.h> #include <readline.h> #include <Python.h> #include <object.h> #include <compile.h> #include <eval.h> int main (int argc, char* argv[]) { int i, j, done = 0; /* lengths of line, code */ char ps1[] = ">>> "; char ps2[] = "... "; char *prompt = ps1; char *msg, *line, *code = NULL; PyObject *src, *glb, *loc; PyObject *exc, *val, *trb, *obj, *dum; Py_Initialize (); loc = PyDict_New (); glb = PyDict_New (); PyDict_SetItemString (glb, "__builtins__", PyEval_GetBuiltins ()); while (!done) { line = readline (prompt); if (NULL == line) /* Ctrl-D pressed */ { done = 1; } else { i = strlen (line); if (i > 0) add_history (line); /* save non-empty lines */ if (NULL == code) /* nothing in code yet */ j = 0; else j = strlen (code); code = realloc (code, i + j + 2); if (NULL == code) /* out of memory */ exit (1); if (0 == j) /* code was empty, so */ code[0] = '\0'; /* keep strncat happy */ strncat (code, line, i); /* append line to code */ code[i + j] = '\n'; /* append '\n' to code */ code[i + j + 1] = '\0'; src = Py_CompileString (code, "<stdin>", Py_single_input); if (NULL != src) /* compiled just fine - */ { if (ps1 == prompt || /* ">>> " or */ '\n' == code[i + j - 1]) /* "... " and double '\n' */ { /* so execute it */ dum = PyEval_EvalCode (src, glb, loc); Py_XDECREF (dum); Py_XDECREF (src); free (code); code = NULL; if (PyErr_Occurred ()) PyErr_Print (); prompt = ps1; } } /* syntax error or E_EOF? */ else if (PyErr_ExceptionMatches (PyExc_SyntaxError)) { PyErr_Fetch (&exc, &val, &trb); /* clears exception! */ if (PyArg_ParseTuple (val, "sO", &msg, &obj) && !strcmp (msg, "unexpected EOF while parsing")) /* E_EOF */ { Py_XDECREF (exc); Py_XDECREF (val); Py_XDECREF (trb); prompt = ps2; } else /* some other syntax error */ { PyErr_Restore (exc, val, trb); PyErr_Print (); free (code); code = NULL; prompt = ps1; } } else /* some non-syntax error */ { PyErr_Print (); free (code); code = NULL; prompt = ps1; } free (line); } } Py_XDECREF(glb); Py_XDECREF(loc); Py_Finalize(); exit(0); }
未定義の g++ シンボル __builtin_new や __pure_virtual を見つけるにはどうしますか?¶
g++ モジュールを動的にロードするには、Python を再コンパイルし、それを g++ で再リンク (Python Modules Makefile 内の LINKCC を変更) し、拡張を g++ でリンク (例えば g++ -shared -o mymodule.so mymodule.o) しなければなりません。