13

私はいくつかの問題に直面しています、そしていくつかの助けが欲しいです。Pythonスクリプトを埋め込むために使用されるピースコードがあります。このPythonスクリプトには、引数として配列を受け取ることを期待する関数が含まれています(この場合、Pythonスクリプト内でnumpy配列を使用しています)。スクリプト内の関数の引数として、Cから埋め込まれたPythonスクリプトに配列を渡す方法を知りたいです。より具体的には、誰かが私にこれの簡単な例を見せてもらえますか?

4

2 に答える 2

21

実際、ここでの最善の答えはnumpy、Cコードからでも、配列を排他的に使用することです。しかし、それが不可能な場合は、C型とPython型の間でデータを共有するコードと同じ問題が発生します。

一般に、CとPythonの間でデータを共有するには、少なくとも5つのオプションがあります。

  1. 渡すPythonlistまたはその他のオブジェクトを作成します。
  2. __getitem__Pythonでシーケンスオブジェクトに定義するのと同じメソッド(など)を使用して、配列をラップして表す新しいPythonタイプを(Cコードで)定義します。
  3. 配列へのポインタをintptr_t、または明示的なctypes型にキャストするか、キャストしないままにします。次にctypes、Python側で使用してアクセスします。
  4. 配列へのポインタをキャストしconst char *str(または、Py3ではbytes)として渡し、Python側でstructまたはを使用してアクセスします。ctypes
  5. bufferプロトコルに一致するオブジェクトを作成し、Python側でstructまたはを再度使用します。ctypes

あなたの場合、numpy.arrayPythonでsを使用したいとします。したがって、一般的なケースは次のようになります。

  1. numpy.array合格するを作成します。
  2. (おそらく適切ではありません)
  3. ポインタをそのまま配列に渡し、Pythonから、配列に変換できるctypes型にポインタを取得するために使用します。numpy
  4. 配列へのポインタをにキャストし、const char *それをstr(または、Py3ではbytes)として渡します。これは、すでにnumpy配列に変換できる型です。
  5. bufferプロトコルに一致するオブジェクトを作成します。これもnumpy直接変換できると思います。

list1の場合、これは非常に単純な例であるという理由だけで、を使用して行う方法です(そして私はすでにそれを書いています…):

PyObject *makelist(int array[], size_t size) {
    PyObject *l = PyList_New(size);
    for (size_t i = 0; i != size; ++i) {
        PyList_SET_ITEM(l, i, PyInt_FromLong(array[i]));
    }
    return l;
}

そして、これに相当するものがあります(Cが削除されないnumpy.arrayことを信頼できると仮定します。オプションの詳細については、ドキュメントの配列の作成を参照してください)。array

PyObject *makearray(int array[], size_t size) {
    npy_int dim = size;
    return PyArray_SimpleNewFromData(1, &dim, (void *)array);
}

とにかく、これをどのように行ってもPyObject *、Cからのように見える(そして単一のrefcountを持つ)ので、関数の引数として渡すことができますが、Python側では、のように見えnumpy.arrayますlist、、bytesまたはその他の適切なもの。

では、実際に関数の引数をどのように渡しますか?コメントで参照したPureEmbeddingのサンプルコードは、これを行う方法を示していますが、実際に何が起こっているのかを説明していません。実際には、拡張ドキュメントには埋め込みドキュメントよりも多くの説明があります。具体的には、CからPython関数を呼び出すことです。また、標準ライブラリのソースコードには、この例がぎっしり詰まっていることに注意してください(ただし、最適化のため、または利用するために更新されていないために、読みにくいものもあります)。新しい簡略化されたCAPI機能の)。

おそらくあなたはすでにそれを持っているので、PythonからPython関数を取得することについての最初の例をスキップしてください。2番目の例(およびその右の段落)は、それを行う簡単な方法を示しています。。を使用して引数タプルを作成しますPy_BuildValue。したがって、上記の関数によって返されmyfuncたリストを使用して、格納されている関数を呼び出したいとします。これがあなたがすることです:mylistmakelist

if (!PyCallable_Check(myfunc)) {
    PyErr_SetString(PyExc_TypeError, "function is not callable?!");
    return NULL;
}
PyObject *arglist = Py_BuildValue("(o)", mylist);
PyObject *result = PyObject_CallObject(myfunc, arglist);
Py_DECREF(arglist);
return result;

もちろん、有効な呼び出し可能オブジェクトがあることが確実な場合は、呼び出し可能チェックをスキップできます。(そして、適切な場合は、最初に取得したときにチェックすることをお勧めしますmyfunc。そうすれば、より早いエラーフィードバックとより良いエラーフィードバックの両方を提供できるからです。)

何が起こっているのかを実際に理解したい場合は、なしで試してくださいPy_BuildValue。ドキュメントにあるように、の2番目の引数[PyObject_CallObject][6]はタプルであり、とPyObject_CallObject(callable_object, args)同等です。apply(callable_object, args)これはと同等callable_object(*args)です。myfunc(mylist)したがって、 Pythonで呼び出したい場合は、myfunc(*(mylist,))それを効果的に変換して、Cに変換できるようにする必要があります。次のtupleように作成できます。

PyObject *arglist = PyTuple_Pack(1, mylist);

ただし、通常Py_BuildValueは簡単で(特に、すべてをPythonオブジェクトとしてまだパックしていない場合)、コードの意図が明確になります(他の方向でPyArg_ParseTuple明示的な関数を使用するよりも、使用する方が単純で明確ですtuple)。

それで、どうやってそれを手に入れますmyfuncか?埋め込みコードから関数を作成した場合は、ポインタをそのままにしておいてください。Pythonコードから渡してほしい場合は、それが最初の例とまったく同じです。たとえば、モジュールやその他のコンテキストから名前で検索する場合、のような具象型とのようなPyModule抽象型のAPIPyMappingは非常に単純であり、Pythonコードを同等のCコードに変換する方法は一般的に明らかです。結果はほとんど醜い定型文です。

すべてをまとめると、整数のC配列があり、intを返すimport mymodule関数を呼び出したいとします。mymodule.myfunc(mylist)簡略化した例を次に示します(実際にはテストされておらず、エラー処理もありませんが、すべての部分が表示されるはずです)。

int callModuleFunc(int array[], size_t size) {
    PyObject *mymodule = PyImport_ImportModule("mymodule");
    PyObject *myfunc = PyObject_GetAttrString(mymodule, "myfunc");
    PyObject *mylist = PyList_New(size);
    for (size_t i = 0; i != size; ++i) {
        PyList_SET_ITEM(l, i, PyInt_FromLong(array[i]));
    }
    PyObject *arglist = Py_BuildValue("(o)", mylist);
    PyObject *result = PyObject_CallObject(myfunc, arglist);
    int retval = (int)PyInt_AsLong(result);
    Py_DECREF(result);
    Py_DECREF(arglist);
    Py_DECREF(mylist);
    Py_DECREF(myfunc);
    Py_DECREF(mymodule);
    return retval;
}

C ++を使用している場合は、おそらく何らかのスコープガード/用務員などを調べたいと思うでしょう。特に適切なエラー処理を開始した後は、これらすべてのPy_DECREF呼び出しを処理します(これは通常、関数を介して初期のreturn NULL呼び出しが行われることを意味します)。C ++ 11またはBoostを使用している場合は、unique_ptr<PyObject, Py_DecRef>必要なのはそれだけかもしれません。

しかし、実際には、C <-> Python通信を大量に行う予定の場合、醜い定型文をすべて減らすためのより良い方法は、Pythonの拡張を改善するために設計された使い慣れたフレームワークをすべて調べることです。<ahref = "http: //www.cython.org "rel =" noreferrer "> Cython、boost :: pythonなど。埋め込みしている場合でも、拡張と同じ作業を効果的に行っているため、同じように役立ちます。

さらに言えば、ドキュメントを検索すると、一部のツールには埋め込み部分を支援するツールもあります。たとえば、CコードとPythonコードの両方を使用して、Cythonでメインプログラムを作成できますcython --embed。指を交差させたり、鶏を犠牲にしたりすることもできますが、それがうまくいけば、驚くほどシンプルで生産的です。Boostを使い始めるのはそれほど簡単ではありませんが、物事をまとめると、ほとんどすべてが期待どおりに行われ、正しく機能します。これは、拡張と同じように埋め込みにも当てはまります。等々。

于 2012-12-18T22:04:23.540 に答える
1

Python関数にはPythonオブジェクトを渡す必要があります。そのPythonオブジェクトをNumPy配列にする必要があるため、配列の作成にはNumPyC-API関数の1つを使用する必要があります。PyArray_SimpleNewFromData()おそらく良いスタートです。データをコピーせずに、提供されたバッファを使用します。

そうは言っても、ほとんどの場合、Pythonでメインプログラムを記述し、CコードにC拡張モジュールを使用する方が簡単です。このアプローチにより、Pythonにメモリ管理を簡単に実行させることができ、ctypesモジュールとNumpyのcpython拡張機能を組み合わせることで、NumPy配列をC関数に簡単に渡すことができます。

于 2012-12-18T22:12:29.790 に答える