Verwendung von np.ndarrays
als ctypes
Argumente
Der beste Ansatz ist die Verwendung von ndpointer
wie in der numpy-docs .
Dieser Ansatz ist flexibler als die Verwendung von z. B., POINTER(c_double), da mehrere Einschränkungen angegeben werden können, die beim Aufruf der Funktion ctypes überprüft werden. Dazu gehören Datentyp Typ, Anzahl der Dimensionen, Form und Flags. Erfüllt ein gegebenes Array nicht die angegebenen Einschränkungen nicht erfüllt, wird ein TypeError ausgelöst.
Minimales, reproduzierbares Beispiel
Aufruf von memcpy von python. Schließlich der Dateiname der Standard-C-Bibliothek libc.so.6
angepasst werden muss.
import ctypes
import numpy as np
n_bytes_f64 = 8
nrows = 2
ncols = 5
clib = ctypes.cdll.LoadLibrary("libc.so.6")
clib.memcpy.argtypes = [
np.ctypeslib.ndpointer(dtype=np.float64, ndim=2, flags='C_CONTIGUOUS'),
np.ctypeslib.ndpointer(dtype=np.float64, ndim=1, flags='C_CONTIGUOUS'),
ctypes.c_size_t]
clib.memcpy.restype = ctypes.c_void_p
arr_from = np.arange(nrows * ncols).astype(np.float64)
arr_to = np.empty(shape=(nrows, ncols), dtype=np.float64)
print('arr_from:', arr_from)
print('arr_to:', arr_to)
print('\ncalling clib.memcpy ...\n')
clib.memcpy(arr_to, arr_from, nrows * ncols * n_bytes_f64)
print('arr_from:', arr_from)
print('arr_to:', arr_to)
Ausgabe
arr_from: [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
arr_to: [[0.0e+000 4.9e-324 9.9e-324 1.5e-323 2.0e-323]
[2.5e-323 3.0e-323 3.5e-323 4.0e-323 4.4e-323]]
calling clib.memcpy ...
arr_from: [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
arr_to: [[0. 1. 2. 3. 4.]
[5. 6. 7. 8. 9.]]
Wenn Sie die ndim=1/2
Argumente von ndpointer
nicht mit den Dimensionen von arr_from/arr_to
scheitert der Code mit einer ArgumentError
.
Da der Titel dieser Frage recht allgemein gehalten ist, ...
Aufbau einer np.ndarray
von einer ctypes.c_void_p
Ergebnis
Minimales, reproduzierbares Beispiel
In dem folgenden Beispiel wird ein Teil des Speichers durch malloc und mit 0en gefüllt durch memset . Dann wird ein Numpy-Array konstruiert, um auf diesen Speicher zuzugreifen. Natürlich gibt es dabei einige Probleme mit den Besitzverhältnissen, da Python Speicher, der in c alloziert wurde, nicht freigibt. Speicherlecks muss man kostenlos den zugewiesenen Speicher wieder durch ctypes. Die copia Methode kann für die np.ndarray
zu erwerben Eigentum .
import ctypes
import numpy as np
n_bytes_int = 4
size = 7
clib = ctypes.cdll.LoadLibrary("libc.so.6")
clib.malloc.argtypes = [ctypes.c_size_t]
clib.malloc.restype = ctypes.c_void_p
clib.memset.argtypes = [
ctypes.c_void_p,
ctypes.c_int,
ctypes.c_size_t]
clib.memset.restype = np.ctypeslib.ndpointer(
dtype=np.int32, ndim=1, flags='C_CONTIGUOUS')
clib.free.argtypes = [ctypes.c_void_p]
clib.free.restype = ctypes.c_void_p
pntr = clib.malloc(size * n_bytes_int)
ndpntr = clib.memset(pntr, 0, size * n_bytes_int)
print(type(ndpntr))
ctypes_pntr = ctypes.cast(ndpntr, ctypes.POINTER(ctypes.c_int))
print(type(ctypes_pntr))
print()
arr_noowner = np.ctypeslib.as_array(ctypes_pntr, shape=(size,))
arr_owner = np.ctypeslib.as_array(ctypes_pntr, shape=(size,)).copy()
# arr_owner = arr_noowner.copy()
print('arr_noowner (at {:}): {:}'.format(arr_noowner.ctypes.data, arr_noowner))
print('arr_owner (at {:}): {:}'.format(arr_owner.ctypes.data, arr_owner))
print('\nfree allocated memory again ...\n')
_ = clib.free(pntr)
print('arr_noowner (at {:}): {:}'.format(arr_noowner.ctypes.data, arr_noowner))
print('arr_owner (at {:}): {:}'.format(arr_owner.ctypes.data, arr_owner))
print('\njust for fun: free some python-memory ...\n')
_ = clib.free(arr_owner.ctypes.data_as(ctypes.c_void_p))
print('arr_noowner (at {:}): {:}'.format(arr_noowner.ctypes.data, arr_noowner))
print('arr_owner (at {:}): {:}'.format(arr_owner.ctypes.data, arr_owner))
Ausgabe
<class 'numpy.ctypeslib.ndpointer_<i4_1d_C_CONTIGUOUS'>
<class '__main__.LP_c_int'>
arr_noowner (at 104719884831376): [0 0 0 0 0 0 0]
arr_owner (at 104719884827744): [0 0 0 0 0 0 0]
free allocated memory again ...
arr_noowner (at 104719884831376): [ -7687536 24381 -28516336 24381 0 0 0]
arr_owner (at 104719884827744): [0 0 0 0 0 0 0]
just for fun: free some python-memory ...
arr_noowner (at 104719884831376): [ -7687536 24381 -28516336 24381 0 0 0]
arr_owner (at 104719884827744): [ -7779696 24381 -28516336 24381 0 0 0]
0 Stimmen
Haben Sie die Kontrolle über die C-Lib? Können Sie die API der Bibliothek ändern?
0 Stimmen
Ja - ich habe die Quelle. Ich bin mir nicht sicher, welchen Weg ich einschlagen soll, da der Pointer-Ansatz es Python ermöglicht, direkt auf die Daten zuzugreifen, was in einigen Fällen ein Vorteil sein könnte. In meinem Fall wäre es jedoch von Vorteil, wenn alles als Zeiger ausgegeben würde.
ctype
Array. Irgendwelche Empfehlungen?2 Stimmen
Ich würde vorschlagen, dass die Bibliothek ein (NumPy-) Array verwendet, das Sie in Python zuweisen und an die Bibliothek weitergeben. Auf diese Weise können Sie auf denselben Speicher zugreifen, müssen sich aber nicht mit umständlichen Konvertierungen herumschlagen. Sie haben bereits ein NumPy-Array, und die Übergabe an eine Bibliothek wird gut unterstützt durch die Verwendung von
numpy.ctypeslib.ndpointer
als Argumenttyp für die ctypes-Verschalung Ihrer Funktion. (Wenn das nicht klar ist, fragen Sie einfach...)