Dua gaya besar dalam mengorganisasi program — functional dan object-oriented — kerap dipertentangkan sebagai kubu yang harus dipilih salah satu. Kenyataannya, Python sejak awal dirancang sebagai bahasa multi-paradigma: kita bisa menulis pure function, memakai class, atau mencampur keduanya dalam satu berkas. Pertanyaan yang berguna bukan “paradigma mana yang menang”, melainkan “untuk masalah yang sedang dihadapi, gaya mana yang membuat kode paling mudah dipahami dan dikembangkan”.
Masalah yang Sama, Dua Gaya #
Kasus ujinya sederhana: keranjang belanja yang bisa menerima item dan menghitung harga setelah diskon. Versi fungsional menempatkan data polos di dictionary dan menjadikan setiap operasi sebagai function yang mengembalikan data baru:
def tambah_item(keranjang, nama, harga):
return {"items": [*keranjang["items"], (nama, harga)]}
def total_belanja(keranjang):
return sum(harga for _, harga in keranjang["items"])
def harga_setelah_diskon(keranjang, persen):
return round(total_belanja(keranjang) * (1 - persen / 100))
keranjang = {"items": []}
keranjang = tambah_item(keranjang, "kopi", 45000)
keranjang = tambah_item(keranjang, "gula", 12000)
print(total_belanja(keranjang))
print(harga_setelah_diskon(keranjang, 10))Output yang Dihasilkan:
57000
51300Perhatikan baris keranjang = tambah_item(keranjang, ...): setiap penambahan item menghasilkan dictionary baru, dan assignment-lah yang memperbarui nama keranjang. Data lama tidak pernah dimodifikasi — disiplin copy-on-write dari Chapter Immutability diterapkan penuh.
Versi object-oriented membungkus data dan perilaku ke dalam satu class — struktur yang sudah kita bangun sejak Chapter Memahami Class dan Object:
class Keranjang:
def __init__(self):
self.items = []
def tambah_item(self, nama, harga):
self.items.append((nama, harga))
def total_belanja(self):
return sum(harga for _, harga in self.items)
def harga_setelah_diskon(self, persen):
return round(self.total_belanja() * (1 - persen / 100))
keranjang = Keranjang()
keranjang.tambah_item("kopi", 45000)
keranjang.tambah_item("gula", 12000)
print(keranjang.total_belanja())
print(keranjang.harga_setelah_diskon(10))Output yang Dihasilkan:
57000
51300Hasil akhirnya identik, tetapi cara kerjanya berbeda. Method tambah_item memodifikasi self.items di tempat — keranjang yang sama berubah seiring waktu, dan tidak ada assignment yang menandai momen perubahan.
Kekuatan dan Kelemahan Kedua Gaya #
| Aspek | Functional | Object-Oriented |
|---|---|---|
| Data dan perilaku | Terpisah: data polos + function bebas | Digabung dalam class |
| Perubahan state | Data baru; data lama tak tersentuh | Mutasi internal (self.items) |
| Paling cocok untuk | Transformasi data, kalkulasi murni | Entitas dengan identitas dan siklus hidup |
| Pengujian | Function berdiri sendiri, langsung diuji | Perlu instance, kadang perlu setup state |
| Kebutuhan struktur | Ringan — cukup function | Lebih banyak struktur (class, self) |
| Jejak perubahan | Eksplisit lewat assignment | Tersembunyi di balik pemanggilan method |
Gaya fungsional bersinar saat program didominasi aliran data: baca, transformasi, simpan. Gaya OOP bersinar saat ada entitas yang hidup lama dan berubah tingkah — sesi pengguna, koneksi database, state mesin game.
Python Sang Multi-Paradigm #
Posisi Python dalam perdebatan ini sejak lama jelas: praktis di atas ideologi. Guido van Rossum berulang kali menegaskan bahwa Python bukan bahasa functional — ia mengambil fitur fungsional yang terbukti berguna dan membuang yang membuat kode sulit dibaca pemula. Kisah paling terkenalnya adalah reduce: pada Python 2 reduce adalah built-in, lalu di Python 3 dipindahkan ke module functools karena loop for eksplisit dianggap lebih jelas untuk kasus umum.
Jejak pragmatisme itu juga terlihat di detail kecil. Builtin sorted(data, key=...) menerima function sebagai argument — gaya fungsional. Method string seperti teks.upper() mengembalikan string baru tanpa memutasi aslinya — semangat immutability. Sementara struktur, framework, dan standard library penuh class — gaya OOP. Python tidak memaksa kita memilih.
Panduan Memilih #
Rangkuman keputusan sehari-hari:
- Pilih gaya fungsional saat mengolah koleksi data, menulis logika bisnis murni yang butuh pengujian padat, atau menyiapkan kode yang akan diparalelkan.
- Pilih class saat data dan perilaku melekat erat, entitas punya identitas yang dilacak lintas panggilan, atau framework yang dipakai (misalnya Django) memang berbasis class.
- Campurlah tanpa rasa bersalah — kombinasi paling umum di kode Python produksi: class tipis menyimpan state, sementara perhitungan berat didelegasikan ke pure function.
Contoh campuran yang wajar dalam satu program:
produk = [("kopi", 45000), ("gula", 12000), ("teh", 25000)]
termurah = sorted(produk, key=lambda p: p[1])[0]
print(termurah)
class Produk:
def __init__(self, nama, harga):
self.nama = nama
self.harga = harga
def harga_diskon(self, persen):
return round(self.harga * (1 - persen / 100))
teh = Produk("teh", 25000)
print(teh.harga_diskon(20))Output yang Dihasilkan:
('gula', 12000)
20000Baris sorted(produk, key=...) adalah functional style: data tidak berubah, function key dikirim sebagai argument. Di bawahnya, class Produk menyimpan identitas dan harga sebagai state miliknya. Keduanya hidup berdampingan tanpa konflik — dan itulah cara Python bekerja: alat banyak, pilihan ada pada kita.