Python adalah bahasa tingkat tinggi yang hebat untuk prototyping dan analisis data, tetapi eksekusinya jauh lebih lambat dari C untuk loop numerik berat. Kabar baiknya: Python dapat memanggil kode C langsung lewat beberapa mekanisme FFI: ctypes (built-in, memuat .so langsung), cffi (mirip ctypes tetapi lebih ergonomis), dan CPython extension (modul native yang ditulis dengan Python C API). Mekanisme yang paling sederhana dan paling portabel adalah ctypes—kita akan fokus pada ini. Pada chapter ini kita akan membangun libmatema.so lalu memanggilnya dari Python dengan ctypes.

Membangun Library C untuk Python #

Library yang dipanggil dari Python tidak ada bedanya dengan library C biasa—kita build dengan -fPIC -shared. Kuncinya: header harus memakai extern "C" agar nama symbol tidak ter-mangle (penting bila library dikompilasi dengan C++). Kita buat libmatema dengan tiga fungsi: mate_tambah, mate_rata_rata, mate_versi.

c
/* File: libmatema.h - deklarasi untuk konsumsi Python */
#ifndef LIBMATEMA_H
#define LIBMATEMA_H

#ifdef __cplusplus
extern "C" {
#endif

int    mate_tambah(int a, int b);
double mate_rata_rata(const double *arr, int n);
const char *mate_versi(void);

#ifdef __cplusplus
}
#endif

#endif /* LIBMATEMA_H */
c
/* File: libmatema.c - implementasi */
#include "libmatema.h"

int mate_tambah(int a, int b) {
    return a + b;
}

double mate_rata_rata(const double *arr, int n) {
    if (n <= 0 || !arr) return 0.0;
    double total = 0.0;
    for (int i = 0; i < n; i++) total += arr[i];
    return total / (double) n;
}

const char *mate_versi(void) {
    return "libmatema 1.0.0";
}

Compile menjadi shared library:

bash
gcc -std=c11 -Wall -Wextra -fPIC -shared -o libmatema.so libmatema.c
ls -la libmatema.so

Output yang Dihasilkan:

text
-rwxrwxr-x 1 z z 15168 Oct 13 09:02 libmatema.so

Mari kita telaah baris-baris kode di atas:

  1. Baris 4-6 (#ifdef __cplusplus extern "C"): Wajib bila library akan dipanggil dari Python ctypes. Tanpa block ini, bila library dikompilasi dengan g++ (bukan gcc), symbol mate_tambah berubah menjadi _Z11mate_tambahii (gaya Itanium ABI C++). ctypes.CDLL mencari mate_tambah persis dan akan gagal.
  2. Baris 13 (const double *arr, int n): Fungsi menerima pointer ke array double plus panjang. Pola ini umum di C—Python harus mengirimkan array lewat ctypes.POINTER(ctypes.c_double).
  3. Flag -fPIC -shared: Sama seperti chapter Shared Library Interfaces. -fPIC menghasilkan Position Independent Code, -shared menghasilkan shared object.

Memanggil dari Python dengan ctypes #

Modul ctypes adalah modul built-in Python yang memuat .so/.dll dan memetakan tipe C ke tipe Python. Tiga langkah utama: (1) muat library, (2) nyatakan tipe argumen dan return value (opsional tapi disarankan), (3) panggil fungsi seperti fungsi Python biasa.

python
#!/usr/bin/env python3
# File: py_panggil.py - memanggil libmatema.so lewat ctypes
import ctypes

# Muat shared library (absolut atau relatif)
lib = ctypes.CDLL("./libmatema.so")

# Tentukan tipe argumen dan return value
lib.mate_tambah.argtypes = [ctypes.c_int, ctypes.c_int]
lib.mate_tambah.restype  = ctypes.c_int

lib.mate_versi.argtypes = []
lib.mate_versi.restype  = ctypes.c_char_p

lib.mate_rata_rata.argtypes = [ctypes.POINTER(ctypes.c_double), ctypes.c_int]
lib.mate_rata_rata.restype  = ctypes.c_double

# 1. Panggil fungsi integer
hasil = lib.mate_tambah(17, 25)
print(f"17 + 25 = {hasil}")

# 2. Panggil fungsi yang return string
versi = lib.mate_versi().decode("utf-8")
print(f"Versi: {versi}")

# 3. Panggil fungsi yang menerima array
data = (ctypes.c_double * 4)(10.0, 20.0, 30.0, 40.0)
rata = lib.mate_rata_rata(data, 4)
print(f"Rata-rata [10,20,30,40] = {rata}")

Jalankan Python:

bash
python3 py_panggil.py

Output yang Dihasilkan:

text
17 + 25 = 42
Versi: libmatema 1.0.0
Rata-rata [10,20,30,40] = 25.0

Mari kita telaah baris-baris kode di atas:

  1. Baris 5 (ctypes.CDLL("./libmatema.so")): Memuat shared library dan mengembalikan objek CDLL. Setiap fungsi global di library menjadi atribut objek—lib.mate_tambah, lib.mate_versi, dst. Bila library tidak ditemukan, Python memunculkan OSError. Path dapat absolut (/usr/lib/libfoo.so) atau relatif (./libmatema.so); gunakan ctypes.util.find_library("matema") untuk pencarian otomatis.
  2. Baris 8-9 (argtypes dan restype): Menyatakan tipe argumen dan return value. Tanpa deklarasi ini, ctypes mengasumsikan int untuk keduanya—bila return value adalah double atau char *, hasil akan salah (misalnya mengembalikan integer acak). Mendeklarasikan argtypes juga membuat ctypes melakukan type checking dan mengkonversi otomatis (misalnya Python int → c_int).
  3. Baris 12 (restype = ctypes.c_char_p): Memberi tahu ctypes bahwa fungsi mengembalikan const char *. Python akan menerima objek bytes (bukan str), jadi kita perlu .decode("utf-8") di baris 22 untuk mendapat string Python.
  4. Baris 21 ((ctypes.c_double * 4)(...)): Membuat array C double[4] dan mengisinya. Konstruktor menerima daftar nilai. Objek ini kompatibel dengan const double * di sisi C—ctypes otomatis mengkonversi c_double_Array_4 ke POINTER(c_double).
  5. Baris 22 (lib.mate_rata_rata(data, 4)): Memanggil fungsi C dengan array dan panjang. Hasil double otomatis menjadi Python float. Loop numerik di C jauh lebih cepat daripada di Python murni, sehingga pola ini sering dipakai untuk akselerasi.

Tiga Pendekatan FFI Python #

ctypes bukan satu-satunya pilihan. Berikut ringkasan tiga pendekatan utama:

PendekatanCara KerjaKelebihanKekurangan
ctypesMemuat .so saat runtime; deklarasi tipe di PythonBuilt-in Python, no compileLambat untuk call sering; manual tipe
cffiMirip ctypes tetapi deklarasi tipe dari C headerLebih cepat, ABI-like API, dapat memakai header asliButuh instalasi cffi
CPython extensionModul native lewat Python C APIPaling cepat, integrasi penuhButuh compile, kompleks

Untuk sebagian besar kasus, ctypes sudah cukup. cffi dipakai oleh library seperti pyOpenSSL yang membungkus banyak fungsi. CPython extension dipakai bila Anda perlu integrasi sangat ketat (misalnya NumPy, yang mendefinisikan tipe array baru di sisi Python).