Python(via ctypes
)でラップされたCライブラリを使用して一連の計算を実行しています。実行のさまざまな段階で、Pythonにデータを取得したいと思います。 numpy
配列。
私が使用しているラッピングは、配列データに対して2つの異なるタイプの戻り値を実行します(これは特に興味深いものです)。
ctypes
Array:type(x)
を実行すると(xはctypes
配列です)、返りに_<class 'module_name.wrapper_class_name.c_double_Array_12000'>
_を取得します。このデータはドキュメントの内部データのコピーであり、numpy
配列に簡単に取得できます。
_>>> np.ctypeslib.as_array(x)
_
これは、データの1D numpy
配列を返します。
ctype
データへのポインタ:この場合、ライブラリのドキュメントから、保存および使用されているデータへのポインタを取得していることを理解しています直接ライブラリに。ホエー私はtype(y)
(yはポインターです)を実行します_<class 'module_name.wrapper_class_name.LP_c_double'>
_を取得します。この場合でも、_y[0][2]
_のようなデータを介してインデックスを作成することはできますが、非常に扱いにくいものを介してnumpyに取得することしかできませんでした。
_>>> np.frombuffer(np.core.multiarray.int_asbuffer(
ctypes.addressof(y.contents), array_length*np.dtype(float).itemsize))
_
私はこれを古いnumpy
メーリングリスト Travis Oliphantからのスレッド で見つけましたが、numpy
のドキュメントでは見つかりませんでした。このアプローチの代わりに私が上記のように試すと、次のようになります:
_>>> np.ctypeslib.as_array(y)
...
... BUNCH OF STACK INFORMATION
...
AttributeError: 'LP_c_double' object has no attribute '__array_interface__'
_
これは_np.frombuffer
_が最善の方法ですか、これを行う唯一の方法ですか?私は他の提案を受け入れていますが、このデータで使用したいnumpy
機能に依存する他の後処理コードがたくさんあるので、numpy
を使用したいと思います。
CtypesポインターオブジェクトからNumPy配列を作成することは問題のある操作です。ポインタが指しているメモリを実際に誰が所有しているかは不明です。いつ再び解放されますか?どのくらいの期間有効ですか?可能な限り、私はこの種の構造を避けようとしました。 Pythonコードで配列を作成してC関数に渡す方が、Python非対応のC関数によって割り当てられたメモリを使用するよりもはるかに簡単で安全です。後者を実行すると、ある程度、高水準言語がメモリ管理を処理する利点があります。
誰かがメモリを処理することを本当に確信している場合は、Python "バッファプロトコル"を公開するオブジェクトを作成し、このバッファオブジェクトを使用してNumPy配列を作成することができます。ドキュメントに記載されていないint_asbuffer()
関数を使用して、投稿にバッファオブジェクトを作成します。
_buffer = numpy.core.multiarray.int_asbuffer(
ctypes.addressof(y.contents), 8*array_length)
_
(np.dtype(float).itemsize
を_8
_に置き換えたことに注意してください。どのプラットフォームでも常に8です。)バッファオブジェクトを作成する別の方法は、PyBuffer_FromMemory()
関数をPython ctypesを介したC API:
_buffer_from_memory = ctypes.pythonapi.PyBuffer_FromMemory
buffer_from_memory.restype = ctypes.py_object
buffer = buffer_from_memory(y, 8*array_length)
_
これらの両方の方法で、buffer
からNumPy配列を作成できます
_a = numpy.frombuffer(buffer, float)
_
(frombuffer
に2番目のパラメーターの代わりに.astype()
を使用する理由が実際にはわかりません。さらに、なぜ_np.int
_を使用するのか不思議です。 double
s。)
こんなに簡単にならないかなと思いますが、それほど悪くはないですね。醜い詳細をすべてラッパー関数に埋め込むことができ、もう心配する必要はありません。
Python 3.でこれらのいずれも機能しませんでした。python 2および3でctypesポインタをnumpy ndarrayに変換するための一般的な解決策として、これが機能することがわかりました(読み取り専用バッファーを取得する):
def make_nd_array(c_pointer, shape, dtype=np.float64, order='C', own_data=True):
arr_size = np.prod(shape[:]) * np.dtype(dtype).itemsize
if sys.version_info.major >= 3:
buf_from_mem = ctypes.pythonapi.PyMemoryView_FromMemory
buf_from_mem.restype = ctypes.py_object
buf_from_mem.argtypes = (ctypes.c_void_p, ctypes.c_int, ctypes.c_int)
buffer = buf_from_mem(c_pointer, arr_size, 0x100)
else:
buf_from_mem = ctypes.pythonapi.PyBuffer_FromMemory
buf_from_mem.restype = ctypes.py_object
buffer = buf_from_mem(c_pointer, arr_size)
arr = np.ndarray(Tuple(shape[:]), dtype, buffer, order=order)
if own_data and not arr.flags.owndata:
return arr.copy()
else:
return arr
別の可能性(最初の回答が書かれたときに利用可能なものよりも新しいバージョンのライブラリが必要になる場合があります-ctypes 1.1.0
およびnumpy 1.5.0b2
)は、ポインターから配列への変換です。
np.ctypeslib.as_array(
(ctypes.c_double * array_length).from_address(ctypes.addressof(y.contents)))
これはまだ共有所有権のセマンティクスを持っているように思われるので、おそらく最終的に基礎となるバッファーを解放することを確認する必要があります。
Pythonで配列を作成することに問題がない場合は、次の2D配列の例がpython3で機能します。
import numpy as np
import ctypes
OutType = (ctypes.c_float * 4) * 6
out = OutType()
YourCfunction = ctypes.CDLL('./yourlib.so').voidreturningfunctionwithweirdname
YourCfunction.argtypes = [ctypes.POINTER(ctypes.c_float)]*3, ctypes.POINTER(ctypes.c_float)]*5, OutType]
YourCfunction(input1, input2, out)
out = np.array(out) # convert it to numpy
print(out)
numpyおよびctypesのバージョンは1.11.1および1.1.0です
np.ctypeslib.as_array
必要なのはここだけです。
配列から:
c_arr = (c_float * 8)()
np.ctypeslib.as_array(c_arr)
ポインターから
c_arr = (c_float * 8)()
ptr = ctypes.pointer(c_arr[0])
np.ctypeslib.as_array(ptr, shape=(8,))