Saya ingin memanggil perpustakaan C dari aplikasi Python. Saya tidak ingin membungkus seluruh API, hanya fungsi dan tipe data yang relevan dengan kasus saya. Seperti yang saya lihat, saya punya tiga pilihan:
- Buat modul ekstensi aktual dalam C. Mungkin berlebihan, dan saya juga ingin menghindari overhead belajar menulis ekstensi.
- Gunakan Cython untuk mengekspos bagian yang relevan dari pustaka C ke Python.
- Lakukan semuanya dengan Python, gunakan
ctypes
untuk berkomunikasi dengan perpustakaan eksternal.
Saya tidak yakin apakah 2) atau 3) adalah pilihan yang lebih baik. Keuntungan dari 3) adalah ituctypes
adalah bagian dari perpustakaan standar, dan kode yang dihasilkan akan menjadi Python murni - walaupun saya tidak yakin seberapa besar sebenarnya keuntungan itu.
Apakah ada lebih banyak keuntungan / kerugian dengan salah satu pilihan? Pendekatan mana yang Anda rekomendasikan?
Sunting: Terima kasih atas semua jawaban Anda, mereka menyediakan sumber yang bagus untuk siapa pun yang ingin melakukan sesuatu yang serupa. Keputusan, tentu saja, masih harus dibuat untuk satu kasus — tidak ada jawaban "Ini adalah hal yang benar". Untuk kasus saya sendiri, saya mungkin akan menggunakan ctypes, tapi saya juga berharap untuk mencoba Cython di beberapa proyek lain.
Dengan tidak adanya jawaban yang benar, menerima jawaban agak sewenang-wenang; Saya memilih jawaban FogleBird karena memberikan beberapa wawasan yang baik tentang ctypes dan saat ini juga merupakan jawaban dengan suara tertinggi. Namun, saya sarankan untuk membaca semua jawaban untuk mendapatkan gambaran yang bagus.
Terima kasih lagi.
Jawaban:
ctypes
adalah taruhan terbaik Anda untuk menyelesaikannya dengan cepat, dan itu menyenangkan untuk dikerjakan karena Anda masih menulis Python!Saya baru-baru ini membungkus FTDI driver untuk berkomunikasi dengan chip USB menggunakan ctypes dan itu hebat. Saya telah menyelesaikan semuanya dan bekerja dalam waktu kurang dari satu hari kerja. (Saya hanya mengimplementasikan fungsi yang kami butuhkan, sekitar 15 fungsi).
Kami sebelumnya menggunakan modul pihak ketiga, PyUSB , untuk tujuan yang sama. PyUSB adalah modul ekstensi C / Python yang sebenarnya. Tapi PyUSB tidak melepaskan GIL saat melakukan pemblokiran baca / tulis, yang menyebabkan masalah bagi kami. Jadi saya menulis modul kami sendiri menggunakan ctypes, yang memang melepaskan GIL saat memanggil fungsi asli.
Satu hal yang perlu diperhatikan adalah bahwa ctypes tidak akan tahu
#define
konstanta dan hal-hal di perpustakaan yang Anda gunakan, hanya fungsinya, jadi Anda harus mendefinisikan ulang konstanta-konstanta tersebut dalam kode Anda sendiri.Berikut ini adalah contoh bagaimana kode tersebut terlihat (banyak yang diambil, hanya mencoba menunjukkan intinya):
Seseorang melakukan beberapa tolok ukur pada berbagai opsi.
Saya mungkin lebih ragu-ragu jika saya harus membungkus pustaka C ++ dengan banyak kelas / template / dll. Tapi ctypes bekerja dengan baik dengan struct dan bahkan dapat dipanggil kembali ke Python.
sumber
ctypes
(untukpyinotify
), tetapi saya ingin memahami masalahnya lebih menyeluruh.One thing to note is that ctypes won't know about #define constants and stuff in the library you're using, only the functions, so you'll have to redefine those constants in your own code.
Jadi, saya harus mendefinisikan konstanta yang ada diwinioctl.h
....ctypes
jauh lebih lambat daripada ekstensi-c karena bottleneck adalah antarmuka dari Python ke CPeringatan: pendapat pengembang inti Cython di depan.
Saya hampir selalu merekomendasikan Cython di atas ctypes. Alasannya adalah karena memiliki jalur peningkatan yang jauh lebih lancar. Jika Anda menggunakan ctypes, banyak hal akan menjadi sederhana pada awalnya, dan tentu saja keren untuk menulis kode FFI Anda dengan Python polos, tanpa kompilasi, membangun dependensi dan sebagainya. Namun, pada titik tertentu, Anda hampir pasti akan menemukan bahwa Anda harus sering menelepon ke perpustakaan C Anda, baik dalam satu lingkaran atau dalam serangkaian panggilan yang saling tergantung yang lebih lama, dan Anda ingin mempercepatnya. Itulah titik di mana Anda akan melihat bahwa Anda tidak dapat melakukannya dengan ctypes. Atau, ketika Anda membutuhkan fungsi panggilan balik dan Anda menemukan bahwa kode panggilan balik Python Anda menjadi hambatan, Anda ingin mempercepatnya dan / atau memindahkannya ke C juga. Sekali lagi, Anda tidak bisa melakukannya dengan ctypes.
Dengan Cython, OTOH, Anda benar-benar bebas untuk membuat kode pembungkus dan panggilan setipis atau setebal yang Anda inginkan. Anda dapat mulai dengan panggilan sederhana ke dalam kode C dari kode Python biasa, dan Cython akan menerjemahkannya ke dalam panggilan C asli, tanpa overhead panggilan tambahan, dan dengan overhead konversi yang sangat rendah untuk parameter Python. Ketika Anda menyadari bahwa Anda memerlukan kinerja yang lebih besar di beberapa titik di mana Anda membuat terlalu banyak panggilan mahal ke pustaka C Anda, Anda bisa mulai membuat anotasi kode Python di sekitarnya dengan tipe statis dan membiarkan Cython mengoptimalkannya langsung ke C untuk Anda. Atau, Anda dapat mulai menulis ulang bagian kode C Anda di Cython untuk menghindari panggilan dan untuk mengkhususkan dan mengencangkan loop Anda secara algoritmik. Dan jika Anda membutuhkan panggilan cepat, cukup tulis fungsi dengan tanda tangan yang sesuai dan berikan langsung ke registri panggilan balik C secara langsung. Sekali lagi, tidak ada overhead, dan itu memberi Anda kinerja panggilan C sederhana. Dan dalam kasus yang jauh lebih kecil kemungkinannya bahwa Anda benar-benar tidak bisa mendapatkan kode Anda cukup cepat di Cython, Anda masih dapat mempertimbangkan untuk menulis ulang bagian yang benar-benar kritis dalam C (atau C ++ atau Fortran) dan menyebutnya dari kode Cython Anda secara alami dan asli. Tapi kemudian, ini benar-benar menjadi pilihan terakhir alih-alih satu-satunya pilihan.
Jadi, ctypes senang melakukan hal-hal sederhana dan dengan cepat menjalankan sesuatu. Namun, begitu hal mulai tumbuh, Anda kemungkinan besar akan sampai pada titik di mana Anda melihat bahwa Anda sebaiknya menggunakan Cython sejak awal.
sumber
__radd__
). Ini sangat menjengkelkan ketika Anda berencana untuk kelas Anda untuk berinteraksi dengan tipe builtin (misalnyaint
danfloat
). Juga, metode ajaib dalam cython hanya sedikit buggy secara umum.Cython adalah alat yang cukup keren, layak untuk dipelajari, dan secara mengejutkan dekat dengan sintaks Python. Jika Anda melakukan komputasi ilmiah apa pun dengan Numpy, maka Cython adalah cara yang harus dilakukan karena terintegrasi dengan Numpy untuk operasi matriks cepat.
Cython adalah superset dari bahasa Python. Anda dapat membuang file Python yang valid padanya, dan itu akan memuntahkan program C yang valid. Dalam hal ini, Cython hanya akan memetakan panggilan Python ke API CPython yang mendasarinya. Ini mungkin menghasilkan speedup 50% karena kode Anda tidak lagi ditafsirkan.
Untuk mendapatkan beberapa optimasi, Anda harus mulai memberi tahu Cython fakta tambahan tentang kode Anda, seperti ketik deklarasi. Jika Anda cukup mengatakannya, itu bisa mendidih kode ke murni C. Artinya, untuk loop di Python menjadi untuk loop di C. Di sini Anda akan melihat peningkatan kecepatan besar-besaran. Anda juga dapat menautkan ke program C eksternal di sini.
Menggunakan kode Cython juga sangat mudah. Saya pikir manual membuatnya terdengar sulit. Anda benar-benar hanya melakukan:
dan kemudian Anda bisa
import mymodule
dalam kode Python Anda dan lupa sepenuhnya bahwa itu dikompilasi ke C.Bagaimanapun, karena Cython sangat mudah untuk diatur dan mulai digunakan, saya sarankan mencoba untuk melihat apakah itu sesuai dengan kebutuhan Anda. Ini tidak akan sia-sia jika ternyata tidak menjadi alat yang Anda cari.
sumber
mymod.pyx
modul dan kemudian lakukanimport pyximport; pyximport.install(); import mymod
dan kompilasi terjadi di belakang layar.runcython mymodule.pyx
. Dan tidak seperti pyximport, Anda dapat menggunakannya untuk tugas penautan yang lebih banyak. Satu-satunya peringatan adalah bahwa akulah yang menulis 20 baris bash untuk itu dan mungkin bias.Untuk memanggil pustaka C dari aplikasi Python ada juga cffi yang merupakan alternatif baru untuk ctypes . Ini membawa tampilan baru untuk FFI:
sumber
Saya akan melemparkan yang lain di luar sana: SWIG
Mudah dipelajari, melakukan banyak hal dengan benar, dan mendukung lebih banyak bahasa sehingga waktu yang dihabiskan untuk mempelajarinya bisa sangat berguna.
Jika Anda menggunakan SWIG, Anda membuat modul ekstensi python baru, tetapi dengan SWIG melakukan sebagian besar tugas berat untuk Anda.
sumber
Secara pribadi, saya akan menulis modul ekstensi dalam C. Jangan terintimidasi oleh ekstensi Python C - mereka tidak sulit sama sekali untuk menulis. Dokumentasinya sangat jelas dan bermanfaat. Ketika saya pertama kali menulis ekstensi C dengan Python, saya pikir butuh sekitar satu jam untuk mencari cara menulis satu - tidak banyak waktu sama sekali.
sumber
ctypes sangat bagus ketika Anda sudah punya gumpalan perpustakaan dikompilasi untuk berurusan dengan (seperti perpustakaan OS). Namun, overhead panggilan sangat parah, jadi jika Anda akan membuat banyak panggilan ke perpustakaan, dan Anda akan tetap menulis kode C (atau setidaknya mengkompilasinya), saya akan mengatakan untuk cython . Ini bukan pekerjaan yang lebih, dan akan jauh lebih cepat dan lebih pythonic untuk menggunakan file pyd yang dihasilkan.
Saya pribadi cenderung menggunakan cython untuk mempercepat kode python (loop dan perbandingan integer adalah dua area di mana cython sangat bersinar), dan ketika ada beberapa kode yang lebih terlibat / pembungkus perpustakaan lain yang terlibat, saya akan beralih ke Boost.Python . Boost.Python bisa jadi rumit untuk diatur, tetapi begitu Anda berhasil, itu membuat membungkus kode C / C ++ secara langsung.
cython juga hebat dalam membungkus numpy (yang saya pelajari dari proses SciPy 2009 ), tapi saya belum pernah menggunakan numpy, jadi saya tidak bisa mengomentari itu.
sumber
Jika Anda sudah memiliki perpustakaan dengan API yang ditentukan, saya pikir
ctypes
adalah pilihan terbaik, karena Anda hanya perlu melakukan sedikit inisialisasi dan kemudian memanggil perpustakaan dengan cara yang biasa Anda lakukan.Saya pikir Cython atau membuat modul ekstensi dalam C (yang tidak terlalu sulit) lebih berguna ketika Anda membutuhkan kode baru, misalnya memanggil perpustakaan itu dan melakukan beberapa tugas yang kompleks, memakan waktu, dan kemudian meneruskan hasilnya ke Python.
Pendekatan lain, untuk program sederhana, secara langsung melakukan proses yang berbeda (dikompilasi secara eksternal), mengeluarkan hasilnya ke output standar dan menyebutnya dengan modul subproses. Terkadang itu pendekatan yang paling mudah.
Misalnya, jika Anda membuat program konsol C yang berfungsi kurang lebih seperti itu
Anda bisa menyebutnya dari Python
Dengan sedikit memformat string, Anda dapat mengambil hasilnya dengan cara apa pun yang Anda inginkan. Anda juga dapat menangkap output kesalahan standar, sehingga cukup fleksibel.
sumber
shell=True
dapat dengan mudah mengakibatkan semacam eksploitasi ketika pengguna benar-benar mendapatkan shell. Tidak apa-apa ketika pengembang adalah satu-satunya pengguna, tetapi di dunia ada banyak tusukan menjengkelkan hanya menunggu sesuatu seperti ini.Ada satu masalah yang membuat saya menggunakan ctypes dan bukan cython dan yang tidak disebutkan dalam jawaban lain.
Menggunakan ctypes hasilnya tidak tergantung pada kompiler yang Anda gunakan sama sekali. Anda dapat menulis perpustakaan menggunakan kurang lebih bahasa apa pun yang dapat dikompilasi ke perpustakaan bersama asli. Tidak masalah, sistem mana, bahasa mana dan kompiler mana. Cython, bagaimanapun, dibatasi oleh infrastruktur. Misalnya, jika Anda ingin menggunakan intel compiler di windows, jauh lebih sulit untuk membuat cython berfungsi: Anda harus "menjelaskan" kompiler ke cython, mengkompilasi ulang sesuatu dengan kompiler yang tepat ini, dll. Yang secara signifikan membatasi portabilitas.
sumber
Jika Anda menargetkan Windows dan memilih untuk membungkus beberapa pustaka C ++ yang dipatenkan, maka Anda mungkin segera menemukan bahwa versi yang berbeda dari
msvcrt***.dll
(Visual C ++ Runtime) sedikit tidak kompatibel.Ini berarti bahwa Anda mungkin tidak dapat menggunakan
Cython
karena hasilnyawrapper.pyd
ditautkan denganmsvcr90.dll
(Python 2.7) ataumsvcr100.dll
(Python 3.x) . Jika pustaka yang Anda bungkus terkait dengan versi runtime yang berbeda, maka Anda kurang beruntung.Kemudian untuk membuat semuanya berfungsi, Anda harus membuat pembungkus C untuk pustaka C ++, tautan dll pembungkus itu dengan versi yang sama
msvcrt***.dll
seperti pustaka C ++ Anda. Dan kemudian gunakanctypes
untuk memuat dll pembungkus linting tangan Anda secara dinamis saat runtime.Jadi ada banyak detail kecil, yang dijelaskan dengan sangat rinci dalam artikel berikut:
"Perpustakaan Asli yang Indah (dengan Python) ": http://lucumr.pocoo.org/2013/8/18/beautiful-native-libraries/
sumber
Ada juga satu kemungkinan untuk menggunakan Introspeksi GObject untuk perpustakaan yang menggunakan GLib .
sumber
Saya tahu ini adalah pertanyaan lama tetapi hal ini muncul di google ketika Anda mencari hal-hal seperti
ctypes vs cython
, dan sebagian besar jawaban di sini ditulis oleh mereka yang sudah mahircython
atauc
yang mungkin tidak mencerminkan waktu sebenarnya yang Anda butuhkan untuk berinvestasi untuk mempelajari hal itu untuk mengimplementasikan solusi Anda. Saya seorang pemula yang lengkap dalam keduanya. Saya belum pernah menyentuhcython
sebelumnya, dan memiliki sedikit pengalamanc/c++
.Selama dua hari terakhir, saya sedang mencari cara untuk mendelegasikan kinerja bagian yang berat dari kode saya ke sesuatu yang lebih rendah daripada python. Saya menerapkan kode saya di
ctypes
danCython
, yang pada dasarnya terdiri dari dua fungsi sederhana.Saya punya daftar string besar yang perlu diproses. Perhatikan
list
danstring
. Kedua tipe tidak cocok dengan tipe dic
, karena string python secara default unicode danc
string tidak. Daftar dalam python BUKAN array dari c.Inilah vonis saya. Gunakan
cython
. Ini terintegrasi lebih lancar ke python, dan lebih mudah untuk dikerjakan secara umum. Ketika terjadi kesalahanctypes
hanya membuat Anda segfault, setidaknyacython
akan memberi Anda kompilasi peringatan dengan jejak tumpukan kapan pun memungkinkan, dan Anda dapat mengembalikan objek python yang valid dengan mudahcython
.Berikut ini adalah akun terperinci tentang berapa banyak waktu yang saya perlukan untuk berinvestasi di keduanya untuk mengimplementasikan fungsi yang sama. Saya melakukan sangat sedikit pemrograman C / C ++:
Ctypes:
c
kode itu berfungsi.c
kode.c
kode ke basis kode yang sebenarnya, dan melihat bahwactypes
tidak cocok denganmultiprocessing
modul karena handlernya tidak dapat dipilih secara default.multiprocessing
modul, dan coba lagi.c
kode saya menghasilkan segfault di basis kode saya meskipun melewati kode pengujian saya. Yah, ini mungkin salah saya karena tidak memeriksa dengan baik dengan kasus tepi, saya mencari solusi cepat.c
kode dan pada iterasi kedua atau ketiga dari loop python yang menggunakannya, saya punyaUnicodeError
tentang tidak men-decoding byte pada beberapa posisi meskipun saya menyandikan dan mendekodekan everthing secara eksplisit.Pada titik ini, saya memutuskan untuk mencari alternatif dan memutuskan untuk mencari
cython
:setuptools
alih - alihdistutils
.setup.py
untuk menggunakan modul yang dikompilasi dalam basis kode saya.multiprocessing
versi basis kode. Berhasil.Sebagai catatan, tentu saja saya tidak mengukur waktu yang tepat untuk investasi saya. Mungkin benar bahwa persepsi saya tentang waktu sedikit untuk diperhatikan karena upaya mental yang diperlukan ketika saya berurusan dengan ctypes. Tetapi harus menyampaikan perasaan berurusan dengan
cython
danctypes
sumber