配列
    Array
    Top > C > Python > Cの関数をPythonから呼び出す
    やりたいこと
     Pythonのnumpy配列をCの関数に渡したい.今回は参照渡しと値渡しの両方を実装する.  ・参照渡し   受け取り側で中身をいじると渡した側にも反映される  ・値渡し   受け取り側でいじっても渡した側に反映されない  値渡しと言っても,参照渡しで受け取った配列の中身を新たに作った配列にコピーするだけなので,やることは参照渡しとあまり変わらない. Pythonからの呼び出し例  c_moduleというCの関数を扱うモジュールを作成する.   inc_array: numpy配列を参照渡しで受け取り,各要素に同じ値を加えて返す.   not_inc_array: numpy配列を値渡しのような形で受け取り,それを操作する.  なお配列は2次元,8バイト浮動小数点数の想定.次元数に関するエラーチェックはここでは入れていないが本当はきちんと入れないといけない.  このように,inc_arrayでは元の配列に変更が反映されるが,not_inc_arrayでは反映されない. Cの関数 func.c Cのラッパー wrap.c コンパイル
    解説: ヘッダー
     PythonとCとの連携ではPythonのオブジェクトをPyObjectという型で扱うので,そのためにPython.hをincludeする必要があった.  今回はnumpyの配列numpy.ndarrayを扱うので,arrayobject.hというヘッダーファイルをincludeする必要がある.  fatal error: numpy/arrayobject.h: No such file or directory  ヘッダーファイルが見つからないというエラーが出ることがある.numpyをpipでインストールしているとこうなるらしい.一度アンインストールしてaptで再インストールすると,このヘッダーファイルも付いてくる模様.  参照:   numpyにarrayobject.hが存在しないのでpandasがインストールできない · Issue #1 · philopon/py3-rdkit  それからパッケージ名はpython-numpyではダメで,python3-numpyだとインストールできた(自分の場合).  参照:   numpy_install - HAIK(QHM),オープンソース活用と実験  管理者権限が無くてaptが使えない場合はソースコードなどからヘッダーファイルを落としてくる必要があると思われる.  1行目の#define ...は,非推奨の機能をオフにするもの.これが無いとコンパイル時に以下のような警告文が出る.  非推奨の機能というのは,例えばPyArrayObject(numpy配列の型)の持っているクラスを直接扱う機能など.
    解説: 参照渡し
    numpy配列をCのポインタ配列として受け取る  ソースコード内の順番とは前後するが,まずはベースとなる関数について. PyArray_TYPE( PyArrayObject *obj )  objのデータ型を取得する.今回は8バイト浮動小数点数と想定しているので,そうでない場合はエラーを返す. PyArray_DATA( PyArrayObject *obj )  objのデータを指示するポインタを取得する.  この関数において変数cは変数arrayinと同じメモリ領域を参照している.これを戻り値として返す. 受け取った配列の中身を操作する  PyArrayObject型のarrayinをgetptr_to_pyarray_doubleでdouble型ポインタ配列に変換したものが変数c.  これの各要素にincを足す. Pythonからの呼び出し用のラッパー PyObject PyArray_FROM_OTF(PyObject *obj, int typenum, int requirements)  objをndarrayに変換する関数.その機能はPyArray_FROM_Oで提供されているが,そこに型の指定などの機能が加わったのがこの関数とのこと.  参照:   https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_FROM_OTF   https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_FROM_O Py_DECREF(PyObject *obj)  とメモリ開放をする手続き.  自身が他のいくつの変数から参照されているかを確認して,それがゼロならメモリを解放する,というものらしい.  参照:   Pythonのオブジェクトへの参照とガベージコレクション #ポエム - Qiita   https://docs.python.org/ja/3.10/c-api/refcounting.html#c.Py_DECREF Pythonから呼び出してみる  最初の例で示したように,配列aの各要素に1.1が足されており,C側でのaに対する操作がPython側にそのまま返ってきていることが確認できる.
    解説: 値渡し的な受け取り
    numpy配列の中身をCのポインタ配列にコピーする  先の関数getptr_to_pyarray_doubleでnumpy配列をポインタ配列として受け取り,その中身を別途定義した配列にコピーする.  この関数も次元数に関する制限は不要なのだが,ここでは2次元という前提で大きさを取得している. 受け取った配列の中身を操作する  py_not_inc_arrayはpy_inc_arrayとほぼ同じ.呼び出す関数がinc_arrayからnot_inc_arrayに変わるだけ. Pythonから呼び出してみる  今度は関数の呼び出しの前後でndarrayの中身が変わっていないことが確認できる.
    解説: コンパイル
     「はじめに」でも述べたように,CでPythonオブジェクト及びnumpy配列を使うのに必要な各ヘッダーファイルPython.h及びarrayobject.hがあるディレクトリを検索対象に含める必要がある(ここでarrayobject.hはnumpyというディレクトリ内にある前提で,numpy/arrayobject.hで指定).  自分の環境ではPython.hは/usr/include/python3.9に,arrayobject.hは/usr/include/python3.9/numpyにあるので,-Iで/usr/include/python3.9を加えている.
    References
    numpyにarrayobject.hが存在しないのでpandasがインストールできない · Issue #1 · philopon/py3-rdkit + https://github.com/philopon/py3-rdkit/issues/1 numpy_install - HAIK(QHM)、オープンソース活用と実験 + https://haik.oi21.net/index.php?numpy_install Pythonのオブジェクトへの参照とガベージコレクション #ポエム - Qiita + https://qiita.com/KMim/items/1c9e162f28309842669e 参照カウント — Python 3.10.17 ドキュメント + https://docs.python.org/ja/3.10/c-api/refcounting.html Data type API — NumPy v2.2 Manual + https://numpy.org/doc/stable/reference/c-api/dtype.html Array API — NumPy v2.2 Manual + https://numpy.org/doc/stable/reference/c-api/array.html