1. 別のアプリケーションへのPythonの埋め込み—Pythonドキュメント

提供:Dev Guides
< PythonPython/docs/3.9/extending/embedding
移動先:案内検索

1.1。 別のアプリケーションへのPythonの埋め込み

前の章では、Pythonを拡張する方法、つまり、C関数のライブラリをアタッチしてPythonの機能を拡張する方法について説明しました。 逆の方法でそれを行うことも可能です。Pythonを埋め込むことでC / C ++アプリケーションを充実させます。 埋め込みは、CまたはC ++ではなくPythonでアプリケーションの機能の一部を実装する機能をアプリケーションに提供します。 これは多くの目的に使用できます。 1つの例は、Pythonでいくつかのスクリプトを作成することにより、ユーザーがアプリケーションをニーズに合わせて調整できるようにすることです。 一部の機能をPythonでより簡単に記述できる場合は、自分で使用することもできます。

Pythonの埋め込みは、Pythonの拡張に似ていますが、完全ではありません。 違いは、Pythonを拡張しても、アプリケーションのメインプログラムはPythonインタープリターのままですが、Pythonを埋め込んだ場合、メインプログラムはPythonとは関係がない可能性があります。代わりに、アプリケーションの一部がPythonインタープリターを呼び出すことがあります。 Pythonコードを実行します。

したがって、Pythonを埋め込む場合は、独自のメインプログラムを提供することになります。 このメインプログラムがしなければならないことの1つは、Pythonインタープリターを初期化することです。 少なくとも、関数 Py_Initialize()を呼び出す必要があります。 コマンドライン引数をPythonに渡すためのオプションの呼び出しがあります。 その後、アプリケーションの任意の部分からインタプリタを呼び出すことができます。

インタプリタを呼び出すには、いくつかの異なる方法があります。Pythonステートメントを含む文字列を PyRun_SimpleString()に渡すか、stdioファイルポインタとファイル名(エラーメッセージでの識別のみ)をに渡すことができます。 PyRun_SimpleFile()。 前の章で説明した下位レベルの操作を呼び出して、Pythonオブジェクトを作成して使用することもできます。

も参照してください

Python / CAPIリファレンスマニュアル
PythonのCインターフェースの詳細は、このマニュアルに記載されています。 必要な情報がたくさんここにあります。


1.1。 非常に高レベルの埋め込み

Pythonを埋め込む最も簡単な形式は、非常に高レベルのインターフェースを使用することです。 このインターフェースは、アプリケーションと直接対話する必要なしにPythonスクリプトを実行することを目的としています。 これは、たとえば、ファイルに対して何らかの操作を実行するために使用できます。

#define PY_SSIZE_T_CLEAN
#include <Python.h>

int
main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }
    Py_SetProgramName(program);  /* optional but recommended */
    Py_Initialize();
    PyRun_SimpleString("from time import time,ctime\n"
                       "print('Today is', ctime(time()))\n");
    if (Py_FinalizeEx() < 0) {
        exit(120);
    }
    PyMem_RawFree(program);
    return 0;
}

Py_SetProgramName()関数は、 Py_Initialize()の前に呼び出して、Pythonランタイムライブラリへのパスについてインタプリタに通知する必要があります。 次に、Pythonインタープリターは Py_Initialize()で初期化され、日付と時刻を出力するハードコードされたPythonスクリプトが実行されます。 その後、 Py_FinalizeEx()呼び出しはインタプリタをシャットダウンし、続いてプログラムが終了します。 実際のプログラムでは、Pythonスクリプトを別のソース、おそらくテキストエディタルーチン、ファイル、またはデータベースから取得したい場合があります。 ファイルからPythonコードを取得するには、 PyRun_SimpleFile()関数を使用すると、メモリスペースの割り当てやファイルの内容の読み込みの手間を省くことができます。


1.2。 非常に高レベルの埋め込みを超えて:概要

高レベルのインターフェースを使用すると、アプリケーションから任意のPythonコードを実行できますが、データ値の交換は控えめに言っても非常に面倒です。 それが必要な場合は、低レベルの呼び出しを使用する必要があります。 より多くのCコードを書かなければならないという犠牲を払って、あなたはほとんど何でも達成することができます。

意図は異なりますが、Pythonの拡張とPythonの埋め込みはまったく同じアクティビティであることに注意してください。 前の章で説明したほとんどのトピックは引き続き有効です。 これを示すために、PythonからCへの拡張コードが実際に何をするかを考えてみましょう。

  1. データ値をPythonからCに変換します。
  2. 変換された値を使用してCルーチンへの関数呼び出しを実行し、
  3. CからPythonへの呼び出しからのデータ値を変換します。

Pythonを埋め込む場合、インターフェースコードは次のことを行います。

  1. データ値をCからPythonに変換し、
  2. 変換された値を使用してPythonインターフェイスルーチンへの関数呼び出しを実行し、
  3. PythonからCへの呼び出しからのデータ値を変換します。

ご覧のとおり、データ変換の手順は、言語間の転送のさまざまな方向に対応するために単純に交換されています。 唯一の違いは、両方のデータ変換間で呼び出すルーチンです。 拡張するときはCルーチンを呼び出し、埋め込むときはPythonルーチンを呼び出します。

この章では、データをPythonからCに、またはその逆に変換する方法については説明しません。 また、参照の適切な使用とエラーの処理が理解されていることを前提としています。 これらの側面はインタプリタの拡張と変わらないため、必要な情報については前の章を参照できます。


1.3。 純粋な埋め込み

最初のプログラムは、Pythonスクリプトで関数を実行することを目的としています。 非常に高レベルのインターフェースに関するセクションと同様に、Pythonインタープリターはアプリケーションと直接対話しません(ただし、次のセクションで変更されます)。

Pythonスクリプトで定義された関数を実行するためのコードは次のとおりです。

#define PY_SSIZE_T_CLEAN
#include <Python.h>

int
main(int argc, char *argv[])
{
    PyObject *pName, *pModule, *pFunc;
    PyObject *pArgs, *pValue;
    int i;

    if (argc < 3) {
        fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
        return 1;
    }

    Py_Initialize();
    pName = PyUnicode_DecodeFSDefault(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\n");
                    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\n", PyLong_AsLong(pValue));
                Py_DECREF(pValue);
            }
            else {
                Py_DECREF(pFunc);
                Py_DECREF(pModule);
                PyErr_Print();
                fprintf(stderr,"Call failed\n");
                return 1;
            }
        }
        else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
        }
        Py_XDECREF(pFunc);
        Py_DECREF(pModule);
    }
    else {
        PyErr_Print();
        fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
        return 1;
    }
    if (Py_FinalizeEx() < 0) {
        return 120;
    }
    return 0;
}

このコードは、argv[1]を使用してPythonスクリプトをロードし、argv[2]で指定された関数を呼び出します。 その整数引数は、argv配列の他の値です。 このプログラムをコンパイルしてリンクし(完成した実行可能ファイル 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_DecodeFSDefault(argv[1]);
/* Error checking of pName left out */
pModule = PyImport_Import(pName);

インタープリターを初期化した後、 PyImport_Import()を使用してスクリプトをロードします。 このルーチンには、引数としてPython文字列が必要です。これは、 PyUnicode_FromString()データ変換ルーチンを使用して作成されます。

pFunc = PyObject_GetAttrString(pModule, argv[2]);
/* pFunc is a new reference */

if (pFunc && PyCallable_Check(pFunc)) {
    ...
}
Py_XDECREF(pFunc);

スクリプトが読み込まれると、 PyObject_GetAttrString()を使用して探している名前が取得されます。 名前が存在し、返されたオブジェクトが呼び出し可能である場合、それは関数であると安全に見なすことができます。 次に、プログラムは通常どおり引数のタプルを作成することによって続行します。 次に、Python関数の呼び出しは次のように行われます。

pValue = PyObject_CallObject(pFunc, pArgs);

関数が返されると、pValueNULLになるか、関数の戻り値への参照が含まれます。 値を確認した後、必ず参照を解放してください。


1.4。 組み込みPythonの拡張

これまで、組み込みPythonインタープリターは、アプリケーション自体から機能にアクセスできませんでした。 Python APIは、組み込みインタープリターを拡張することでこれを可能にします。 つまり、組み込みインタプリタは、アプリケーションによって提供されるルーチンで拡張されます。 複雑に聞こえますが、それほど悪くはありません。 アプリケーションが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()関数のすぐ上に挿入します。 また、 Py_Initialize()を呼び出す前に、次の2つのステートメントを挿入します。

numargs = argc;
PyImport_AppendInittab("emb", &PyInit_emb);

これらの2行は、numargs変数を初期化し、emb.numargs()関数を組み込みPythonインタープリターにアクセスできるようにします。 これらの拡張機能を使用すると、Pythonスクリプトは次のようなことができます。

import emb
print("Number of arguments", emb.numargs())

実際のアプリケーションでは、メソッドはアプリケーションのAPIをPythonに公開します。


1.5。 PythonをC ++に埋め込む

PythonをC ++プログラムに埋め込むことも可能です。 これがどのように行われるかは、使用されるC ++システムの詳細によって異なります。 一般に、メインプログラムをC ++で記述し、C ++コンパイラを使用してプログラムをコンパイルおよびリンクする必要があります。 C ++を使用してPython自体を再コンパイルする必要はありません。


1.6。 Unixライクなシステムでのコンパイルとリンク

特にPythonはC動的拡張機能(.so)として実装されたライブラリモジュールをロードする必要があるため、Pythonインタープリターをアプリケーションに埋め込むためにコンパイラー(およびリンカー)に渡す適切なフラグを見つけることは必ずしも簡単ではありません。ファイル)それに対してリンクされています。

必要なコンパイラフラグとリンカフラグを見つけるには、インストールプロセスの一部として生成されるpythonX.Y-configスクリプトを実行できます(python3-configスクリプトも使用できる場合があります)。 このスクリプトにはいくつかのオプションがあり、そのうち次のオプションが直接役立ちます。

  • pythonX.Y-config --cflagsは、コンパイル時に推奨されるフラグを提供します。

    $ /opt/bin/python3.4-config --cflags
    -I/opt/include/python3.4m -I/opt/include/python3.4m -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
  • pythonX.Y-config --ldflagsは、リンク時に推奨されるフラグを表示します。

    $ /opt/bin/python3.4-config --ldflags
    -L/opt/lib/python3.4/config-3.4m -lpthread -ldl -lutil -lm -lpython3.4m -Xlinker -export-dynamic

ノート

複数のPythonインストール間(特にシステムPythonと独自にコンパイルされたPython間)の混乱を避けるために、上記の例のように、pythonX.Y-configへの絶対パスを使用することをお勧めします。


この手順が機能しない場合(すべてのUnixライクなプラットフォームで機能することが保証されているわけではありませんが、バグレポートを歓迎します)、ダイナミックリンクやシステムのドキュメントを読む必要があります。 PythonのMakefilesysconfig.get_makefile_filename()を使用してその場所を見つけます)とコンパイルオプションを調べます。 この場合、 sysconfig モジュールは、組み合わせたい構成値をプログラムで抽出するための便利なツールです。 例えば:

>>> import sysconfig
>>> sysconfig.get_config_var('LIBS')
'-lpthread -ldl  -lutil'
>>> sysconfig.get_config_var('LINKFORSHARED')
'-Xlinker -export-dynamic'