ネストpythonインタプリタ(Embedding Python in Another Application)
hello,world
5. Embedding Python in Another Application
前述の章では,Pythonを拡張する方法,すなわち,C関数ライブラリに接続することでPythonの機能を拡張する方法について議論した.Pythonを内蔵することで、C/C++アプリケーションを豊富にする方法もあります.組み込みにより、CまたはC++しか使用できないのではなく、Pythonでいくつかの機能を実現することができます.これはいろいろな用途があります.1つの例は、Pythonスクリプトを作成することで、必要なアプリケーションをカスタマイズできるようにすることです.Pythonで書く機能があれば、それを使うことも考えられます.
内蔵Pythonは拡張Pythonと似ていますが、必ずしも同じではありません.違いは、Pythonを拡張するときはメインプログラムがPython解釈器であり、Pythonを内蔵すると、メインプログラムはPythonとは関係ありません.逆に、アプリケーションの一部がPython解釈器を呼び出してPythonコードを実行するだけです.
Pythonを内蔵するには、独自のメインプログラムを提供する必要があります.メインプログラムがしなければならないことの一つはPython解釈器を初期化することです.少なくとも、関数Pyを呼び出します.Initialize().コマンドラインパラメータをPythonに転送することもできます.次に、アプリケーションのどこからでも解釈器を呼び出すことができます.
ここでは、Python文を含む文字列をPyRun_に渡すいくつかの呼び出し解釈器の違いがあります.SimpleString();あるいはstdioファイルポインタとファイル名(エラー情報の識別のみのため)をPyRun_に渡すSimpleFile().前の章で説明したLower-level操作を呼び出してPythonオブジェクトを構築および使用することもできます.
5.1. Very High Level Embedding
最も簡単な埋め込みPythonの形式は高級インターフェース(the verf high level interface)を使用する.このインタフェースは、アプリケーションと直接対話する必要がなく、Pythonスクリプトを実行します.これは、ファイルを操作する場合に適しています.
Py_SetProgramName()はPy_Initialize()は、Pythonランタイムライブラリ(Python run-time libraries)のパスを解釈器に伝えるために呼び出されます.続いて、Pythonインタプリタ用Py_Initialize()を初期化し、ハードコーディングされたPythonスクリプトと出力日時を実行します.続いて、Py_Finalize()はインタプリタを閉じ、プログラムは終了します.実際のプログラムでは、pythonコードを別の場所から取得する必要があります.テキストエディタ、ファイル、データベースなどです.一つのファイルからPythonコードを取得するにはPyRun_を使いますSimpleFile()は、メモリの割り当てやファイルの内容の読み取りの手間を省くのに適しています.
5.2. Beyond Very High Level Embedding: An overview
高度なインタフェースでは、アプリケーションで任意のコードフラグメントを実行できますが、データの交換はかなり面倒です.必要があれば、低レベルインタフェース(lower level calls)を使用する必要があります.Cコードをたくさん書いたが、私たちはもっと多くのことをすることができます.
目的の違いを除いて、拡張Pythonと内蔵Pythonは似ていることに注意してください(is quite the same activity).前の章で議論したほとんどの話題はここで依然として有効である.これを示すために、PythonのCコードを拡張して実際に何をしたのかを考えてみましょう.
1.PythonからCへデータを変換し、
2.変換したデータでCプログラムの関数呼び出し(Perform a function call to a C routine using the converted values)を実行し、
3.関数の戻り値をCからPythonに変換する
Python、インタフェースコードを内蔵すること:
1.CからPythonにデータを変換し、
2.変換されたデータを用いてPythonインタフェースプログラムの関数呼び出し(Perform a function call to a Python interface routine using the converted values)を実行し、
3.関数の戻り値をPythonからCに変換する
データ変換のステップは、言語間で異なる方向に伝達される単純なパッケージにすぎないことがわかります.唯一の違いは、2回のデータ変換の間に呼び出されたプログラムです.拡張時にCプログラムを呼び出し、内蔵時にPythonプログラムを呼び出します.
この章では、PythonからCまたは逆方向にデータを変換する方法については議論しません.引用の合理的な使用とエラー処理も私たちが理解したと仮定します.これらは拡張解釈器と変わらないので、前の章を参照してこれらの情報を理解することができます.
5.3. Pure Embedding
最初のプログラムの目的はPythonスクリプトの関数を実行することです.高度なインタフェースの部分と同様に、Python解釈器はアプリケーションと直接対話しません(次の説明の内容は対話します).
次のコードは、Pythonスクリプトの関数を実行します.
5.4. Extending Embedded Python
これまでネストされたPython解釈器はアプリケーション自体の機能にアクセスしていなかった.Python APIは、Python解釈器を拡張することによってこのアクセスを提供する.すなわち,埋め込まれたPython解釈器がアプリケーションによって提供される関数(routines)を拡張した.複雑に聞こえますが、実はそんなに大げさではありません.Pythonインタプリタを起動することを一時的に忘れます.逆に、アプリケーションをサブルーチンのセットとして、Pythonがこれらのプログラムにアクセスできるように、通常のPython拡張を書くように、いくつかの接着コードを書きました.例:
5.5. Embedding Python in C++
PythonをC++プログラムに埋め込んでもいいです.どのように実現するかはシステムC++の詳細を見る.一般的には、C++でメインプログラムを書き、C++コンパイラでプログラムをコンパイルしてリンクする必要があります.C++でPythonを再コンパイルする必要はありません.
5.6. Compiling and Linking under Unix-like systems
Python解釈器を私たちのアプリケーションに埋め込むには、コンパイラとリンク器のために正しいタグ(find the right flags to pass to your compiler(and linker))を探す必要はありません.PythonがロードするライブラリモジュールはCで実現される動的拡張(C dynamic extensions)であるからです.
必要なコンパイラとリンクタグを見つけるためにPythonXを実行できます.Y-configスクリプト.このスクリプトはインストールプロセスの一部として生成されます(python 3-configスクリプトも使用可能です).このスクリプトにはいくつかのオプションがありますが、次の機能は私たちにとってより直接的になります.
pythonX.Y-config--cflagsは、コンパイルに推奨するタグを提供します.
なお、上記の例では、Python(特にシステムPythonと私たち自身がコンパイルPython)との間の混乱を回避するためにpythonXを用いる.Y-configの場合は絶対パスを使うべきです.
もしこのプログラムがうまくいかなかったら(このプログラムがすべてのクラスUnix(Unix-like)プラットフォームで正常であることを保証しません;バグへの報告を歓迎しますhttp://docs.python.org/3/bugs.html#reporting-bugs))、動的リンクに関するシステムのドキュメントを読むか、PythonのMakefile(sysconfig.get_makefile_filename()をチェックしなければなりません(http://docs.python.org/3/library/sysconfig.html#sysconfig.get_makefile_filename)も参照してください.この場合sysconfig(http://docs.python.org/3/library/sysconfig.html#module-sysconfig)モジュールは、統合したい構成の値をプログラミングで抽出するのに役立つツールです.例:
[dongsong@bogon python_study]$ cat py.cpp
#include <Python.h>
int main(int argc, char** argv)
{
Py_Initialize();
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
PyObject* pModule = PyImport_ImportModule("helloword");
PyObject* pFunc = PyObject_GetAttrString(pModule, "hello");
PyEval_CallObject(pFunc, NULL);
Py_Finalize();
return 0;
}
[dongsong@bogon python_study]$ cat helloword.py
def hello():
print 'hello,world!'
[dongsong@bogon python_study]$ g++ -o py py.cpp -I/home/dongsong/venv/include/python2.6/ -lpython2.6
[dongsong@bogon python_study]$ ./py
hello,world!
公式原書http://docs.python.org/3/extending/embedding.html 5. Embedding Python in Another Application
前述の章では,Pythonを拡張する方法,すなわち,C関数ライブラリに接続することでPythonの機能を拡張する方法について議論した.Pythonを内蔵することで、C/C++アプリケーションを豊富にする方法もあります.組み込みにより、CまたはC++しか使用できないのではなく、Pythonでいくつかの機能を実現することができます.これはいろいろな用途があります.1つの例は、Pythonスクリプトを作成することで、必要なアプリケーションをカスタマイズできるようにすることです.Pythonで書く機能があれば、それを使うことも考えられます.
内蔵Pythonは拡張Pythonと似ていますが、必ずしも同じではありません.違いは、Pythonを拡張するときはメインプログラムがPython解釈器であり、Pythonを内蔵すると、メインプログラムはPythonとは関係ありません.逆に、アプリケーションの一部がPython解釈器を呼び出してPythonコードを実行するだけです.
Pythonを内蔵するには、独自のメインプログラムを提供する必要があります.メインプログラムがしなければならないことの一つはPython解釈器を初期化することです.少なくとも、関数Pyを呼び出します.Initialize().コマンドラインパラメータをPythonに転送することもできます.次に、アプリケーションのどこからでも解釈器を呼び出すことができます.
ここでは、Python文を含む文字列をPyRun_に渡すいくつかの呼び出し解釈器の違いがあります.SimpleString();あるいはstdioファイルポインタとファイル名(エラー情報の識別のみのため)をPyRun_に渡すSimpleFile().前の章で説明したLower-level操作を呼び出してPythonオブジェクトを構築および使用することもできます.
5.1. Very High Level Embedding
最も簡単な埋め込みPythonの形式は高級インターフェース(the verf high level interface)を使用する.このインタフェースは、アプリケーションと直接対話する必要がなく、Pythonスクリプトを実行します.これは、ファイルを操作する場合に適しています.
#include <Python.h>
int
main(int argc, char *argv[])
{
Py_SetProgramName(argv[0]); /* optional but recommended */
Py_Initialize();
PyRun_SimpleString("from time import time,ctime
"
"print('Today is', ctime(time()))
");
Py_Finalize();
return 0;
}
Py_SetProgramName()はPy_Initialize()は、Pythonランタイムライブラリ(Python run-time libraries)のパスを解釈器に伝えるために呼び出されます.続いて、Pythonインタプリタ用Py_Initialize()を初期化し、ハードコーディングされたPythonスクリプトと出力日時を実行します.続いて、Py_Finalize()はインタプリタを閉じ、プログラムは終了します.実際のプログラムでは、pythonコードを別の場所から取得する必要があります.テキストエディタ、ファイル、データベースなどです.一つのファイルからPythonコードを取得するにはPyRun_を使いますSimpleFile()は、メモリの割り当てやファイルの内容の読み取りの手間を省くのに適しています.
5.2. Beyond Very High Level Embedding: An overview
高度なインタフェースでは、アプリケーションで任意のコードフラグメントを実行できますが、データの交換はかなり面倒です.必要があれば、低レベルインタフェース(lower level calls)を使用する必要があります.Cコードをたくさん書いたが、私たちはもっと多くのことをすることができます.
目的の違いを除いて、拡張Pythonと内蔵Pythonは似ていることに注意してください(is quite the same activity).前の章で議論したほとんどの話題はここで依然として有効である.これを示すために、PythonのCコードを拡張して実際に何をしたのかを考えてみましょう.
1.PythonからCへデータを変換し、
2.変換したデータでCプログラムの関数呼び出し(Perform a function call to a C routine using the converted values)を実行し、
3.関数の戻り値をCからPythonに変換する
Python、インタフェースコードを内蔵すること:
1.CからPythonにデータを変換し、
2.変換されたデータを用いてPythonインタフェースプログラムの関数呼び出し(Perform a function call to a Python interface routine using the converted values)を実行し、
3.関数の戻り値をPythonからCに変換する
データ変換のステップは、言語間で異なる方向に伝達される単純なパッケージにすぎないことがわかります.唯一の違いは、2回のデータ変換の間に呼び出されたプログラムです.拡張時にCプログラムを呼び出し、内蔵時にPythonプログラムを呼び出します.
この章では、PythonからCまたは逆方向にデータを変換する方法については議論しません.引用の合理的な使用とエラー処理も私たちが理解したと仮定します.これらは拡張解釈器と変わらないので、前の章を参照してこれらの情報を理解することができます.
5.3. Pure Embedding
最初のプログラムの目的はPythonスクリプトの関数を実行することです.高度なインタフェースの部分と同様に、Python解釈器はアプリケーションと直接対話しません(次の説明の内容は対話します).
次のコードは、Pythonスクリプトの関数を実行します.
#include <Python.h>
int
main(int argc, char *argv[])
{
PyObject *pName, *pModule, *pDict, *pFunc;
PyObject *pArgs, *pValue;
int i;
if (argc < 3) {
fprintf(stderr,"Usage: call pythonfile funcname [args]
");
return 1;
}
Py_Initialize();
pName = PyUnicode_FromString(argv[1]);
/* Error checking of pName left out */
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (pModule != NULL) {
pFunc = PyObject_GetAttrString(pModule, argv[2]);
/* pFunc is a new reference */
if (pFunc && PyCallable_Check(pFunc)) {
pArgs = PyTuple_New(argc - 3);
for (i = 0; i < argc - 3; ++i) {
pValue = PyLong_FromLong(atoi(argv[i + 3]));
if (!pValue) {
Py_DECREF(pArgs);
Py_DECREF(pModule);
fprintf(stderr, "Cannot convert argument
");
return 1;
}
/* pValue reference stolen here: */
PyTuple_SetItem(pArgs, i, pValue);
}
pValue = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
if (pValue != NULL) {
printf("Result of call: %ld
", PyLong_AsLong(pValue));
Py_DECREF(pValue);
}
else {
Py_DECREF(pFunc);
Py_DECREF(pModule);
PyErr_Print();
fprintf(stderr,"Call failed
");
return 1;
}
}
else {
if (PyErr_Occurred())
PyErr_Print();
fprintf(stderr, "Cannot find function \"%s\"
", argv[2]);
}
Py_XDECREF(pFunc);
Py_DECREF(pModule);
}
else {
PyErr_Print();
fprintf(stderr, "Failed to load \"%s\"
", argv[1]);
return 1;
}
Py_Finalize();
return 0;
}
上記のコードはargv[1]で指定されたPythonスクリプトをロードし、argv[2]で指定された名前の関数を呼び出します.関数の整数パラメータはargv配列の他の値です.コンパイルとリンク(compile and link)を行うとhttp://docs.python.org/3/extending/embedding.html#compiling)このプログラム(最終的な実行可能ファイルは「call」と名付けられています)は、Pythonスクリプトを実行します.たとえば、次のようにします.def multiply(a,b):
print("Will compute", a, "times", b)
c = 0
for i in range(0, a):
c = c + b
return c
そして結果は次のとおりです.$call multiply multiply 3 2
Will compute 3 times 2
Result of call: 6
プログラムはその機能に比べて十分大きいが、ほとんどのコードはPythonとCの間のデータ変換とエラー報告をしている.興味深いことに、Pythonを埋め込むルールを遵守し、次のコードを先頭にしています(初期化解釈器):Py_Initialize();
pName = PyUnicode_FromString(argv[1]);
/* Error checking of pName left out */
pModule = PyImport_Import(pName);
インタプリタを初期化した後、スクリプト用PyImport_Import()( http://docs.python.org/3/c-api/import.html#PyImport_Import)を入力します.この関数にはPyUnicode_を使用したPython文字列がパラメータとして必要です.FromString()( http://docs.python.org/3/c-api/unicode.html#PyUnicode_FromString)データ変換関数を使用して構築されます.pFunc = PyObject_GetAttrString(pModule, argv[2]);
/* pFunc is a new reference */
if (pFunc && PyCallable_Check(pFunc)) {
...
}
Py_XDECREF(pFunc);
スクリプトがロードされると、必要な名前はPyObject_を使用できます.GetAttrString()( http://docs.python.org/3/c-api/object.html#PyObject_GetAttrString)をクリックします.名前が存在し、返されるオブジェクトが呼び出し可能である場合、私たちは安全に関数であることを認定することができます.その後、プログラムは正常に実行され、メタグループパラメータが構築されます.次に、Python関数を呼び出します.pValue = PyObject_CallObject(pFunc, pArgs);
関数が返されると、pValueはNULLですか、関数が返される値の参照が含まれますか.戻り値を検出したら参照を解放してください.5.4. Extending Embedded Python
これまでネストされたPython解釈器はアプリケーション自体の機能にアクセスしていなかった.Python APIは、Python解釈器を拡張することによってこのアクセスを提供する.すなわち,埋め込まれたPython解釈器がアプリケーションによって提供される関数(routines)を拡張した.複雑に聞こえますが、実はそんなに大げさではありません.Pythonインタプリタを起動することを一時的に忘れます.逆に、アプリケーションをサブルーチンのセットとして、Pythonがこれらのプログラムにアクセスできるように、通常のPython拡張を書くように、いくつかの接着コードを書きました.例:
static int numargs=0;
/* Return the number of arguments of the application command line */
static PyObject*
emb_numargs(PyObject *self, PyObject *args)
{
if(!PyArg_ParseTuple(args, ":numargs"))
return NULL;
return PyLong_FromLong(numargs);
}
static PyMethodDef EmbMethods[] = {
{"numargs", emb_numargs, METH_VARARGS,
"Return the number of arguments received by the process."},
{NULL, NULL, 0, NULL}
};
static PyModuleDef EmbModule = {
PyModuleDef_HEAD_INIT, "emb", NULL, -1, EmbMethods,
NULL, NULL, NULL, NULL
};
static PyObject*
PyInit_emb(void)
{
return PyModule_Create(&EmbModule);
}
上記のコードをmain()関数の前に挿入します.同時に、次の2行のコードをPy_に挿入します.Initialize()の呼び出しの前に.numargs = argc;
PyImport_AppendInittab("emb", &PyInit_emb);
この2行はnumargs変数を初期化し、emb.numargs()関数は、埋め込まれたPython解釈器によってアクセスできます.これらの拡張があれば、Pythonスクリプトは次のことをすることができます.import emb
print("Number of arguments", emb.numargs())
実際の応用の中で、これらの方法は応用のAPIをPythonに暴露する.5.5. Embedding Python in C++
PythonをC++プログラムに埋め込んでもいいです.どのように実現するかはシステムC++の詳細を見る.一般的には、C++でメインプログラムを書き、C++コンパイラでプログラムをコンパイルしてリンクする必要があります.C++でPythonを再コンパイルする必要はありません.
5.6. Compiling and Linking under Unix-like systems
Python解釈器を私たちのアプリケーションに埋め込むには、コンパイラとリンク器のために正しいタグ(find the right flags to pass to your compiler(and linker))を探す必要はありません.PythonがロードするライブラリモジュールはCで実現される動的拡張(C dynamic extensions)であるからです.
必要なコンパイラとリンクタグを見つけるためにPythonXを実行できます.Y-configスクリプト.このスクリプトはインストールプロセスの一部として生成されます(python 3-configスクリプトも使用可能です).このスクリプトにはいくつかのオプションがありますが、次の機能は私たちにとってより直接的になります.
pythonX.Y-config--cflagsは、コンパイルに推奨するタグを提供します.
$ /opt/bin/python3.3-config --cflags
-I/opt/include/python3.3m -I/opt/include/python3.3m -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
pythonX.Y-config--ldflagsは、リンクに推奨されるタグを提供します.$ /opt/bin/python3.3-config --ldflags
-L/opt/lib/python3.3/config-3.3m -lpthread -ldl -lutil -lm -lpython3.3m -Xlinker -export-dynamic
なお、上記の例では、Python(特にシステムPythonと私たち自身がコンパイルPython)との間の混乱を回避するためにpythonXを用いる.Y-configの場合は絶対パスを使うべきです.
もしこのプログラムがうまくいかなかったら(このプログラムがすべてのクラスUnix(Unix-like)プラットフォームで正常であることを保証しません;バグへの報告を歓迎しますhttp://docs.python.org/3/bugs.html#reporting-bugs))、動的リンクに関するシステムのドキュメントを読むか、PythonのMakefile(sysconfig.get_makefile_filename()をチェックしなければなりません(http://docs.python.org/3/library/sysconfig.html#sysconfig.get_makefile_filename)も参照してください.この場合sysconfig(http://docs.python.org/3/library/sysconfig.html#module-sysconfig)モジュールは、統合したい構成の値をプログラミングで抽出するのに役立つツールです.例:
>>> import sysconfig
>>> sysconfig.get_config_var('LIBS')
'-lpthread -ldl -lutil'
>>> sysconfig.get_config_var('LINKFORSHARED')
'-Xlinker -export-dynamic'