Satu ndarray pada dasarnya adalah blok memori datar berisi byte, ditambah metadata yang menjelaskan cara menafsirkannya: dtype, shape, dan strides. Pandangan ini yang membuat NumPy bisa bergandengan dengan dunia C — bahasa yang juga bekerja langsung atas blok memori. Ketika perpustakaan C, driver perangkat, atau file biner menyerahkan pointer dan jumlah byte, NumPy dapat membungkusnya menjadi array tanpa menyalin satu byte pun; sebaliknya, array NumPy dapat dioper ke fungsi C sebagai pointer biasa.
Bab ini membedah empat perangkat jembatan itu: strides sebagai peta tata letak, np.frombuffer untuk membaca byte mentah, ctypes untuk mengoper pointer, dan buffer protocol Python. Fondasi konsep tata letaknya sudah disiapkan pada Chapter Memahami Memory Layout dan Strides dan Memory Access.
Memory Layout dan Strides #
Atribut strides menyatakan berapa byte yang harus dilompati untuk berpindah satu langkah di setiap sumbu. Dua array berisi angka sama dapat punya strides berbeda karena tata letak memorinya berbeda:
import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.int32)
print(a.strides)
print(a.flags["C_CONTIGUOUS"])
f = np.asfortranarray(a)
print(f.strides)
print(f.flags["F_CONTIGUOUS"])Output yang Dihasilkan:
(12, 4)
True
(4, 8)
TrueMari kita bedah:
- Baris 4 (
a.strides): array berlayout baris (C order). Berpindah satu kolom melompati 4 byte (satuint32), sedangkan berpindah satu baris melompati3 x 4 = 12byte. - Baris 5: flag
C_CONTIGUOUSmemastikan seluruh baris tersambung rapat di memori — kondisi yang dibahas pada Chapter Contiguous vs Non-contiguous. - Baris 6-8:
np.asfortranarraymenyalin data ke layout kolom; stridesnya menjadi(4, 8)dan flagF_CONTIGUOUSyang aktif. Kode C yang mengharapkan matriks kolom-utama (konvensi Fortran/BLAS) membutuhkan bentuk ini.
frombuffer: Membaca Byte Mentah dari C #
Perpustakaan C biasanya menyerahkan data sebagai void* plus jumlah byte. Di sisi Python, data itu tiba sebagai objek bytes. Fungsi np.frombuffer menafsirkan byte tersebut sesuai dtype — termasuk structured dtype untuk record campuran. Contoh berikut memakai byte hasil struct C { int id; float suhu; } dua record:
import numpy as np
buffer_c = bytes([1, 0, 0, 0, 0, 0, 220, 65,
2, 0, 0, 0, 154, 153, 217, 65])
dt = np.dtype([("id", "i4"), ("suhu", "f4")])
data = np.frombuffer(buffer_c, dtype=dt)
print(data)
print(data["suhu"])Output yang Dihasilkan:
[(1, 27.5) (2, 27.2)]
[27.5 27.2]Bedah baris demi baris:
- Baris 3-4 (
bytes([...])): enam belas byte mentah, dua record masing-masing 8 byte. Empat byte pertama setiap record adalah integer little-endian (1, 0, 0, 0berarti1), empat byte berikutnya adalah pola bit float32. - Baris 5-6 (
np.frombuffer(...)): interpreter membagi buffer per 8 byte dan memetakan field sesuai dtype. Panjang buffer harus kelipatandt.itemsize, jika tidakValueErrordimunculkan. - Baris 8 (
data["suhu"]): hasilnya array structured biasa; field dapat dibaca dan dihitung seperti bab-bab sebelumnya. Catatan penting: array hasilfrombufferbersifat read-only terhadap bufferbytes.
ctypes: Mengoper Pointer ke Fungsi C #
Modul ctypes bawaan Python adalah jalur dua arah menuju fungsi C. Properti arr.ctypes membungkus array agar dapat dioper sebagai pointer dengan tipe eksplisit, dan perubahan lewat pointer itu terbaca langsung di array:
import ctypes
import numpy as np
v = np.array([10, 20, 30], dtype=np.int32)
print(type(v.ctypes))
p = v.ctypes.data_as(ctypes.POINTER(ctypes.c_int32))
print(p[0], p[1], p[2])
p[1] = 99
print(v)Output yang Dihasilkan:
<class 'numpy._core._internal._ctypes'>
10 20 30
[10 99 30]Penjelasan:
- Baris 5 (
v.ctypes): objek pembungkus ini menyimpan alamat buffer array; alamatnya berbeda di tiap eksekusi, karena itu contoh mencetak tipenya, bukan alamatnya. Alamat mentah tersedia sebagaiv.ctypes.databerupa bilangan bulat. - Baris 6 (
data_as(...)):data_asmenghasilkan pointer bertipeint32*— bentuk yang persis diharapkan prototipe fungsi C sepertivoid proses(int32_t* data, int n). - Baris 7-8: pembacaan
p[0],p[1],p[2]menunjuk byte array secara langsung; penugasanp[1] = 99menulis ke memori yang sama, danvlangsung berubah. Tidak ada salinan di jalur ini.
Buffer Protocol dengan memoryview #
Python punya mekanisme standar untuk berbagi memori antarobjek: buffer protocol. ndarray mendukungnya, sehingga array dapat dibungkus memoryview atau diterima fungsi mana pun yang mengharuskan objek buffer — tanpa konversi dan tanpa salinan:
import numpy as np
v = np.array([10, 20, 30], dtype=np.int32)
mv = memoryview(v)
print(mv.format, mv.shape)
print(bytes(mv) == v.tobytes())Output yang Dihasilkan:
i (3,)
TrueBedah singkat:
- Baris 4 (
memoryview(v)):mvmenampung buffer yang sama denganv; atributformatbernilai"i"(kode struct untukint32) danshape(3,). - Baris 5:
bytes(mv)mengekspor isi buffer; hasilnya identik denganv.tobytes(). Objek sepertibytes,bytearray, danarray.arrayjuga mendukung buffer protocol, sehingga pertukaran dua arah dengan array NumPy mengalir tanpa salinan.
Ekosistem di Sekitar Jembatan Ini #
| Jalur | Arah | Peran utama |
|---|---|---|
np.frombuffer | byte luar -> array | menafsirkan buffer read-only sesuai dtype |
arr.ctypes | array -> pointer C | mengoper array ke fungsi ctypes |
| buffer protocol | dua arah | berbagi memori dengan objek Python |
np.lib.format | file .npy | format biner bawaan NumPy yang memuat dtype + shape |