Pointer di Python: apa gunanya?


Jika Anda pernah bekerja dengan bahasa tingkat rendah seperti C atau C ++, Anda mungkin pernah mendengar tentang pointer. Mereka memungkinkan Anda untuk sangat meningkatkan efektivitas potongan kode yang berbeda. Tetapi mereka juga dapat membingungkan pemula - dan bahkan pengembang yang berpengalaman - dan menyebabkan bug manajemen memori. Apakah ada pointer di Python, bisakah saya meniru mereka?

Pointer banyak digunakan dalam C dan C ++. Bahkan, ini adalah variabel yang berisi alamat memori di mana variabel lain berada. Untuk membaca petunjuk, baca ulasan ini.

Berkat artikel ini, Anda akan lebih memahami model objek dengan Python dan mencari tahu mengapa pointer sebenarnya tidak ada dalam bahasa ini. Jika Anda perlu mensimulasikan perilaku pointer, Anda akan belajar cara meniru mereka tanpa mimpi buruk yang menyertai manajemen memori.

Dengan artikel ini, Anda:

  • Pelajari mengapa Python tidak memiliki petunjuk.
  • Pelajari perbedaan antara variabel C dan nama dalam Python.
  • Belajar meniru pointer dengan Python.
  • Gunakan ctypes bereksperimen dengan pointer nyata.

Catatan : Di sini, istilah "Python" diterapkan pada implementasi Python di C, yang dikenal sebagai CPython. Semua diskusi perangkat bahasa berlaku untuk CPython 3.7, tetapi mungkin tidak sesuai dengan iterasi berikutnya.

Mengapa tidak ada pointer di Python?


Saya tidak tahu. Bisakah pointer ada di Python secara asli? Mungkin, tetapi tampaknya, petunjuknya bertentangan dengan konsep Zen Python , karena mereka memprovokasi perubahan implisit alih-alih yang eksplisit. Pointer seringkali cukup kompleks, terutama untuk pemula. Selain itu, mereka mendorong Anda ke keputusan yang gagal atau untuk melakukan sesuatu yang sangat berbahaya, seperti membaca dari area memori, di mana Anda seharusnya tidak membacanya.

Python mencoba untuk mengabstraksi detail implementasi dari pengguna, seperti alamat memori. Seringkali dalam bahasa ini, penekanannya pada kegunaan, bukan kecepatan. Oleh karena itu pointer dengan Python tidak masuk akal. Tapi jangan khawatir, bahasa default memberi Anda beberapa manfaat menggunakan pointer.

Untuk memahami pointer dalam Python, mari kita membahas fitur implementasi bahasa secara singkat. Secara khusus, Anda perlu memahami:

  1. Apa itu benda yang bisa berubah dan tidak berubah.
  2. Bagaimana variabel / nama diatur dalam Python.

Pegang alamat memori Anda, ayo pergi!

Objek dalam Python


Segala sesuatu di Python adalah objek. Misalnya, buka REPL dan lihat bagaimana isinstance() :

 >>> isinstance(1, object) True >>> isinstance(list(), object) True >>> isinstance(True, object) True >>> def foo(): ... pass ... >>> isinstance(foo, object) True 

Kode ini menunjukkan bahwa segala sesuatu di Python sebenarnya adalah objek. Setiap objek mengandung setidaknya tiga jenis data:

  • Penghitung referensi.
  • Jenis
  • Nilai.

Penghitung referensi digunakan untuk mengelola memori. Rincian tentang manajemen ini ditulis dalam Memory Management in Python . Jenis ini digunakan pada tingkat CPython untuk memberikan keamanan jenis selama runtime. Dan nilai adalah nilai aktual yang terkait dengan objek.

Tetapi tidak semua benda itu sama. Ada satu perbedaan penting: objek bisa berubah dan tidak berubah. Memahami perbedaan antara jenis-jenis objek ini akan membantu Anda lebih memahami lapisan pertama bawang yang disebut "pointer di Python."

Benda yang bisa berubah dan tidak berubah


Ada dua jenis objek di Python:

  1. Objek yang tidak dapat diubah (tidak dapat diubah);
  2. Objek yang dapat dimodifikasi (dapat berubah).

Menyadari perbedaan ini adalah kunci pertama untuk melakukan perjalanan melalui dunia pointer di Python. Berikut ini adalah karakterisasi ketidakmampuan dari beberapa tipe yang populer:

Jenis
Abadi?
int
Ya
mengapung
Ya
bool
Ya
kompleks
Ya
tuple
Ya
frozenset
Ya
str
Ya
daftar
Tidak
diatur
Tidak
dikt
Tidak

Seperti yang Anda lihat, banyak tipe primitif yang umum digunakan tidak berubah. Anda dapat memverifikasi ini dengan menulis beberapa kode Python. Anda akan membutuhkan dua alat dari perpustakaan standar:

  1. id() mengembalikan alamat memori objek;
  2. is mengembalikan True jika dan hanya jika dua objek memiliki alamat memori yang sama.

Anda dapat menjalankan kode ini di lingkungan REPL:

 >>> x = 5 >>> id(x) 94529957049376 

Di sini kita mengatur variabel x ke 5 . Jika Anda mencoba mengubah nilai menggunakan tambahan, Anda akan mendapatkan objek baru:

 >>> x += 1 >>> x 6 >>> id(x) 94529957049408 

Walaupun sepertinya kode ini hanya mengubah nilai x , pada kenyataannya Anda mendapatkan objek baru sebagai jawaban.

Jenis str juga tidak berubah:

 >>> s = "real_python" >>> id(s) 140637819584048 >>> s += "_rocks" >>> s 'real_python_rocks' >>> id(s) 140637819609424 

Dan dalam hal ini, s setelah operasi += mendapatkan alamat memori yang berbeda .

Bonus : Operator += menerjemahkan ke berbagai panggilan metode.

Untuk beberapa objek, seperti daftar, += convert ke __iadd__() (append lokal). Ini akan berubah sendiri dan mengembalikan ID yang sama. Namun, str dan int tidak memiliki metode ini, dan sebagai hasilnya, __add__() akan dipanggil alih-alih __iadd__() .

Lihat dokumentasi model data Python untuk lebih jelasnya .

Ketika kami mencoba untuk secara langsung mengubah nilai string dari s kami mendapatkan kesalahan:

 >>> s[0] = "R" 

Jejak balik (panggilan terbaru ditampilkan terakhir):

  File "<stdin>", line 1, in <mdule> TypeError: 'str' object does not support item assignment 

Kode di atas lumpuh dan laporan Python bahwa str tidak mendukung perubahan ini, yang sesuai dengan definisi kekekalan dari tipe str .

Bandingkan dengan objek yang bisa berubah, misalnya, dengan daftar:

 >>> my_list = [1, 2, 3] >>> id(my_list) 140637819575368 >>> my_list.append(4) >>> my_list [1, 2, 3, 4] >>> id(my_list) 140637819575368 

Kode ini menunjukkan perbedaan utama antara kedua jenis objek. Awalnya, my_list memiliki ID. Bahkan setelah menambahkan 4 ke daftar, my_list masih memiliki ID yang sama . Alasannya adalah bahwa list jenis bisa berubah.

Berikut ini adalah demonstrasi lain dari daftar yang dapat diubah menggunakan penugasan:

 >>> my_list[0] = 0 >>> my_list [0, 2, 3, 4] >>> id(my_list) 140637819575368 

Dalam kode ini, kami mengubah my_list dan menetapkannya ke 0 sebagai elemen pertama. Namun, daftar tersebut mempertahankan ID yang sama setelah operasi ini. Langkah selanjutnya di jalur kami untuk belajar Python adalah menjelajahi ekosistemnya.

Kami berurusan dengan variabel


Variabel dalam Python secara fundamental berbeda dari variabel dalam C dan C ++. Pada dasarnya, mereka tidak ada di Python. Alih-alih variabel, ada nama .

Mungkin kedengarannya luar biasa, dan sebagian besar memang demikian. Paling sering, Anda bisa menggunakan nama dalam Python sebagai variabel, tetapi Anda harus memahami perbedaannya. Ini sangat penting ketika Anda mempelajari topik yang sulit seperti petunjuk.

Untuk membuatnya lebih mudah bagi Anda untuk memahami, mari kita lihat bagaimana variabel bekerja di C, apa yang mereka wakili, dan kemudian membandingkannya dengan karya nama dalam Python.

Variabel dalam C


Ambil kode yang mendefinisikan variabel x :

 int x = 2337; 

Eksekusi garis pendek ini melewati beberapa tahap yang berbeda:

  1. Mengalokasikan cukup memori untuk suatu nomor.
  2. Penugasan 2337 ke lokasi memori ini.
  3. Pemetaan yang x menunjukkan nilai ini.

Memori yang disederhanakan mungkin terlihat seperti ini:



Di sini, variabel x memiliki alamat palsu 0x7f1 dan nilai 2337 . Jika nanti Anda ingin mengubah nilai x , Anda dapat melakukan ini:

 x = 2338; 

Kode ini menetapkan variabel x nilai baru 2338 , sehingga menimpa nilai sebelumnya . Ini berarti bahwa variabel x berubah . Skema memori yang diperbarui untuk nilai baru:



Harap dicatat bahwa lokasi x tidak berubah, hanya nilainya sendiri. Ini penting. Ini memberitahu kita bahwa x adalah tempat di memori , dan bukan hanya nama.

Anda juga dapat mempertimbangkan masalah ini sebagai bagian dari konsep kepemilikan. Di satu sisi, x memiliki tempat dalam memori. Pertama, x adalah kotak kosong yang hanya bisa berisi satu bilangan bulat, di mana nilai integer dapat disimpan.

Saat Anda menetapkan x beberapa nilai, Anda memasukkan nilai ke dalam kotak milik x . Jika Anda ingin memperkenalkan variabel baru y , Anda dapat menambahkan baris ini:

 int y = x; 

Kode ini menciptakan kotak baru bernama y dan menyalin nilai dari x ke dalamnya. Sekarang sirkuit memori terlihat seperti ini:



Perhatikan lokasi baru y - 0x7f5 . Meskipun nilai x disalin ke x , variabel y memiliki alamat baru di memori. Oleh karena itu, Anda dapat menimpa nilai y tanpa mempengaruhi x :

 y = 2339; 

Sekarang sirkuit memori terlihat seperti ini:



Saya ulangi: Anda mengubah nilai y , tetapi bukan lokasi. Selain itu, Anda tidak memengaruhi variabel asli x .

Dengan nama dalam Python, situasinya benar-benar berbeda.

Nama dengan Python


Tidak ada variabel dalam Python, hanya nama. Anda dapat menggunakan istilah "variabel" sesuai kebijaksanaan Anda, namun penting untuk mengetahui perbedaan antara variabel dan nama.

Mari kita ambil kode yang setara dari contoh C di atas dan tulis dengan Python:

 >>> x = 2337 

Seperti dalam C, kode melewati beberapa langkah terpisah selama eksekusi ini:

  1. PyObject dibuat.
  2. Nomor untuk PyObject diberikan kode ketik.
  3. 2337 diberi nilai untuk PyObject.
  4. Nama x dibuat.
  5. x menunjuk ke PyObject baru.
  6. Jumlah referensi PyObject bertambah 1.

Catatan : PyObject tidak sama dengan objek dalam Python, entitas ini khusus untuk CPython dan mewakili struktur dasar dari semua objek Python.

PyObject didefinisikan sebagai struktur-C, jadi jika Anda bertanya-tanya mengapa Anda tidak bisa langsung memanggil kode ketik atau penghitung referensi, maka alasannya adalah bahwa Anda tidak memiliki akses langsung ke struktur. Metode panggilan seperti sys.getrefcount () dapat membantu mendapatkan beberapa hal internal.

Jika kita berbicara tentang memori, maka akan terlihat seperti ini:



Di sini, sirkuit memori sangat berbeda dari sirkuit di C yang ditunjukkan di atas. Alih-alih memiliki x memiliki blok memori yang menyimpan nilai 2337 , objek Python yang baru dibuat memiliki memori yang hidup di 2337 . Nama Python x tidak secara langsung memiliki alamat di memori, seperti halnya variabel C memiliki sel statis.

Jika Anda ingin menetapkan x nilai baru, coba kode ini:

 >>> x = 2338 

Perilaku sistem akan berbeda dari apa yang terjadi di C, tetapi itu tidak akan terlalu berbeda dari ikatan asli dengan Python.

Dalam kode ini:

  • PyObject baru dibuat.
  • Nomor untuk PyObject diberikan kode ketik.
  • 2 diberi nilai untuk PyObject.
  • x menunjuk ke PyObject baru.
  • Jumlah referensi dari PyObject baru bertambah 1.
  • Jumlah referensi dari PyObject lama dikurangi sebesar 1.

Sekarang sirkuit memori terlihat seperti ini:



Ilustrasi ini menunjukkan bahwa x menunjuk ke referensi ke objek dan tidak memiliki area memori seperti sebelumnya. Anda juga melihat bahwa perintah x = 2338 bukan tugas, tetapi lebih merupakan pengikatan nama x ke tautan.

Selain itu, objek sebelumnya (berisi nilai 2337 ) sekarang ada dalam memori dengan jumlah referensi 0, dan akan dihapus oleh pengumpul sampah .

Anda dapat memasukkan nama baru y , seperti dalam contoh C:

 >>> y = x 

Nama baru akan muncul di memori, tetapi tidak harus berupa objek baru:



Sekarang Anda melihat bahwa objek Python baru belum dibuat, hanya nama baru yang dibuat yang menunjuk ke objek yang sama. Selain itu, penghitung referensi objek bertambah 1. Anda dapat memeriksa kesetaraan identitas objek untuk mengonfirmasi identitas mereka:

 >>> y is x True 

Kode ini menunjukkan bahwa x dan y adalah satu objek. Tapi jangan salah: Anda masih abadi. Misalnya, Anda dapat melakukan operasi tambahan dengan y :

 >>> y += 1 >>> y is x False 

Setelah penambahan dipanggil, Anda akan mengembalikan objek Python baru. Sekarang memori terlihat seperti ini:



Objek baru telah dibuat, dan sekarang Anda menunjuk ke sana. Sangat mengherankan bahwa kita akan mendapatkan keadaan akhir yang persis sama jika kita secara langsung menautkan y ke 2339 :

 >>> y = 2339 

Setelah ungkapan ini, kami memperoleh memori terakhir, seperti dalam operasi penambahan. Biarkan saya mengingatkan Anda bahwa dengan Python Anda tidak menetapkan variabel, tetapi ikat nama ke tautan.

Tentang magang di Python


Sekarang Anda mengerti bagaimana objek baru dibuat dengan Python dan bagaimana nama dilampirkan. Sudah waktunya untuk berbicara tentang objek yang diinternir.

Kami memiliki kode Python ini:

 >>> x = 1000 >>> y = 1000 >>> x is y True 

Seperti sebelumnya, x dan y adalah nama yang menunjuk ke objek Python yang sama. Tetapi objek ini yang berisi nilai 1000 tidak selalu dapat memiliki alamat memori yang sama. Misalnya, jika Anda menambahkan dua angka dan mendapatkan 1000, Anda akan mendapatkan alamat lain:

 >>> x = 1000 >>> y = 499 + 501 >>> x is y False 

Kali ini, string x is y mengembalikan False . Jika Anda malu, jangan khawatir. Inilah yang terjadi ketika kode ini dieksekusi:

  1. Objek Python dibuat ( 1000 ).
  2. Itu diberi nama x .
  3. Objek Python dibuat ( 499 ).
  4. Objek Python dibuat ( 501 ).
  5. Dua objek ini bertambah.
  6. Objek Python baru dibuat ( 1000 ).
  7. Dia diberi nama y .

Penjelasan Teknis : Langkah-langkah yang dijelaskan hanya terjadi ketika kode ini dijalankan di dalam REPL. Jika Anda mengambil contoh di atas, tempel ke file dan jalankan, maka baris x is y akan mengembalikan True .

Alasannya adalah kecerdasan cepat dari kompiler CPython, yang mencoba melakukan optimasi lubang intip yang membantu untuk menyimpan langkah-langkah eksekusi kode sebanyak mungkin. Detail dapat ditemukan dalam kode sumber CPython optimizer peyphole .

Tapi bukankah itu boros? Ya, tapi Anda membayar harga ini untuk semua manfaat besar Python. Anda tidak perlu berpikir untuk menghapus objek perantara tersebut, dan Anda bahkan tidak perlu tahu tentang keberadaannya! Leluconnya adalah bahwa operasi ini dilakukan relatif cepat, dan Anda tidak akan mengetahuinya sampai saat itu.

Pembuat Python dengan bijaksana memperhatikan overhead ini dan memutuskan untuk membuat beberapa optimasi. Hasilnya adalah perilaku yang mungkin mengejutkan pemula:

 >>> x = 20 >>> y = 19 + 1 >>> x is y True 

Dalam contoh ini, kodenya hampir sama seperti di atas, kecuali kita mendapatkan True . Ini semua tentang benda yang diinternir. Python pra-membuat subset objek tertentu dalam memori dan menyimpannya dalam namespace global untuk penggunaan sehari-hari.

Objek mana yang bergantung pada implementasi Python? Dalam CPython 3.7, interniran adalah:

  1. Bilangan bulat mulai dari -5 hingga 256 .
  2. String yang hanya berisi huruf, angka, atau garis bawah ASCII.

Ini karena variabel-variabel ini sangat sering digunakan dalam banyak program. Dengan magang, Python mencegah alokasi memori untuk objek yang persisten.

Baris dengan ukuran kurang dari 20 karakter dan berisi huruf, angka, atau garis bawah ASCII akan diinternir karena seharusnya digunakan sebagai pengidentifikasi:

 >>> s1 = "realpython" >>> id(s1) 140696485006960 >>> s2 = "realpython" >>> id(s2) 140696485006960 >>> s1 is s2 True 

Di sini s1 dan s2 menunjuk ke alamat yang sama dalam memori. Jika kami tidak memasukkan huruf, angka, atau garis bawah ASCII, kami akan mendapatkan hasil yang berbeda:

 >>> s1 = "Real Python!" >>> s2 = "Real Python!" >>> s1 is s2 False 

Contoh ini menggunakan tanda seru, sehingga string tidak diinternir dan objek yang berbeda dalam memori.

Bonus : Jika Anda ingin objek ini merujuk ke objek yang diinternir yang sama, Anda dapat menggunakan sys.intern() . Salah satu cara untuk menggunakan fitur ini dijelaskan dalam dokumentasi:

Pemanggangan string berguna untuk sedikit meningkatkan kinerja pencarian kamus: jika kunci dalam kamus dan kunci yang akan dicari diinternir, maka perbandingan kunci (setelah hashing) dapat dilakukan dengan membandingkan pointer daripada string. ( Sumber )

Internee sering membingungkan programmer. Ingatlah bahwa jika Anda mulai ragu, Anda selalu dapat menggunakan id() dan untuk menentukan kesetaraan objek.

Emulasi Pointer Python


Fakta bahwa pointer tidak ada secara asli di Python tidak berarti bahwa Anda tidak dapat memanfaatkan pointer. Sebenarnya ada beberapa cara untuk meniru pointer dengan Python. Di sini kita melihat dua di antaranya:

  1. Gunakan sebagai pointer ke tipe yang bisa berubah.
  2. Menggunakan objek Python yang disiapkan khusus.

Gunakan sebagai pointer jenis bisa berubah


Anda sudah tahu jenis apa yang bisa berubah. Berkat mutabilitas mereka, kita dapat meniru perilaku pointer. Katakanlah Anda perlu mereplikasi kode ini:

 void add_one(int *x) { *x += 1; } 

Kode ini membawa pointer ke angka ( *x ) dan menambah nilainya dengan 1. Berikut adalah fungsi utama untuk mengeksekusi kode:

 #include <stdi.h> int main(void) { int y = 2337; printf("y = %d\n", y); add_one(&y); printf("y = %d\n", y); return 0; } 

Dalam fragmen di atas, kami menetapkan y ke 2337 , menampilkan nilai saat ini, meningkatkannya dengan 1, dan kemudian menampilkan nilai baru. Berikut ini muncul di layar:

 y = 2337 y = 2338 

Salah satu cara untuk mereplikasi perilaku ini dengan Python adalah dengan menggunakan tipe yang bisa berubah. Misalnya, terapkan daftar dan ubah elemen pertama:

 >>> def add_one(x): ... x[0] += 1 ... >>> y = [2337] >>> add_one(y) >>> y[0] 2338 

Di sini add_one(x) merujuk ke elemen pertama dan meningkatkan nilainya dengan 1. Menggunakan daftar berarti bahwa sebagai hasilnya kita mendapatkan nilai yang diubah. Jadi ada pointer di Python? Tidak. Perilaku yang dijelaskan menjadi mungkin karena daftar adalah tipe yang bisa berubah. Jika Anda mencoba menggunakan tuple, Anda mendapatkan kesalahan:

 >>> z = (2337,) >>> add_one(z) 

Jejak balik (panggilan terbaru terakhir):

  File "<stdin>", line 1, in <module> File "<stdin>", line 2, in add_one TypeError: 'tuple' object does not support item assignment 

Kode ini menunjukkan ketidakstabilan tuple, sehingga tidak mendukung penetapan elemen.

list bukan satu-satunya tipe yang bisa berubah, pointer bagian juga ditiru menggunakan dict .

Misalkan Anda memiliki aplikasi yang harus melacak terjadinya peristiwa menarik. Ini dapat dilakukan dengan membuat kamus dan menggunakan salah satu elemennya sebagai penghitung:

 >>> counters = {"func_calls": 0} >>> def bar(): ... counters["func_calls"] += 1 ... >>> def foo(): ... counters["func_calls"] += 1 ... bar() ... >>> foo() >>> counters["func_calls"] 2 

Dalam contoh ini, kamus menggunakan penghitung untuk melacak jumlah panggilan fungsi. Setelah memanggil foo() penghitung bertambah 2, seperti yang diharapkan. Dan semua berkat dict .

Jangan lupa, ini hanya emulasi dari perilaku pointer, ini tidak ada hubungannya dengan pointer nyata di C dan C ++. Kita dapat mengatakan bahwa operasi ini lebih mahal daripada jika dilakukan dalam C atau C ++.

Menggunakan Objek Python


dict adalah cara yang bagus untuk meniru pointer dengan Python, tetapi terkadang membosankan untuk mengingat nama kunci yang Anda gunakan. Terutama jika Anda menggunakan kamus di berbagai bagian aplikasi. Kelas Python khusus dapat membantu di sini.

Katakanlah Anda perlu melacak metrik dalam suatu aplikasi. Cara terbaik untuk mengabaikan detail yang menjengkelkan adalah membuat kelas:

 class Metrics(object): def __init__(self): self._metrics = { "func_calls": 0, "cat_pictures_served": 0, } 

Kode ini mendefinisikan kelas Metrics . Masih menggunakan kamus untuk menyimpan data terbaru yang terletak pada _metrics anggota _metrics . Ini akan memberi Anda mutabilitas yang diperlukan. Sekarang Anda hanya perlu mengakses nilai-nilai ini. Anda dapat melakukan ini menggunakan properti:

 class Metrics(object): # ... @property def func_calls(self): return self._metrics["func_calls"] @property def cat_pictures_served(self): return self._metrics["cat_pictures_served"] 

Di sini kita menggunakan @ properti . Jika Anda baru mengenal dekorator, baca artikel Primer tentang Python Dekorator . Dalam hal ini, dekorator @property memungkinkan Anda untuk mengakses func_calls dan cat_pictures_served , seolah-olah mereka adalah atribut:

 >>> metrics = Metrics() >>> metrics.func_calls 0 >>> metrics.cat_pictures_served 0 

Fakta bahwa Anda dapat merujuk nama-nama ini sebagai atribut berarti bahwa Anda disarikan dari fakta bahwa nilai-nilai ini disimpan dalam kamus. Selain itu, Anda membuat nama atribut lebih eksplisit. Tentu saja, Anda harus dapat meningkatkan nilai:

 class Metrics(object): # ... def inc_func_calls(self): self._metrics["func_calls"] += 1 def inc_cat_pics(self): self._metrics["cat_pictures_served"] += 1 

:

  1. inc_func_calls()
  2. inc_cat_pics()

metrics . , , :

 >>> metrics = Metrics() >>> metrics.inc_func_calls() >>> metrics.inc_func_calls() >>> metrics.func_calls 2 

func_calls inc_func_calls() Python. , - metrics , .

: , inc_func_calls() inc_cat_pics() @property.setter int , .

Metrics :

 class Metrics(object): def __init__(self): self._metrics = { "func_calls": 0, "cat_pictures_served": 0, } @property def func_calls(self): return self._metrics["func_calls"] @property def cat_pictures_served(self): return self._metrics["cat_pictures_served"] def inc_func_calls(self): self._metrics["func_calls"] += 1 def inc_cat_pics(self): self._metrics["cat_pictures_served"] += 1 

ctypes


, - Python, CPython? ctypes , C. ctypes, Extending Python With C Libraries and the ยซctypesยป Module .

, , . - add_one() :

 void add_one(int *x) { *x += 1; } 

, x 1. , (shared) . , add.c , gcc:

 $ gcc -c -Wall -Werror -fpic add.c $ gcc -shared -o libadd1.so add.o 

C add.o . libadd1.so .

libadd1.so . ctypes Python:

 >>> import ctypes >>> add_lib = ctypes.CDLL("./libadd1.so") >>> add_lib.add_one <_FuncPtr object at 0x7f9f3b8852a0> 

ctypes.CDLL , libadd1 . add_one() , , Python-. , . Python , .

, ctypes :

 >>> add_one = add_lib.add_one >>> add_one.argtypes = [ctypes.POINTER(ctypes.c_int)] 

, C. , , :

 >>> add_one(1) Traceback (most recent call last): File "<stdin>", line 1, in <module> ctypes.ArgumentError: argument 1: <class 'TypeError'>: \ expected LP_c_int instance instead of int 

Python , add_one() , . , ctypes . :

 >>> x = ctypes.c_int() >>> x c_int(0) 

x 0 . ctypes byref() , .

: .

, . , .

add_one() :

 >>> add_one(ctypes.byref(x)) 998793640 >>> x c_int(1) 

Hebat! 1. , Python .

Kesimpulan


Python . , Python.

Python:

  • .
  • Python- .
  • ctypes.

Python .

Source: https://habr.com/ru/post/id454324/


All Articles