Domain perpustakaan tidak punya aturan bawaan yang harus dijaga ketat — judul yang salah cuma salah ketik. Domain keuangan lain ceritanya: saldo rekening bank tidak boleh negatif, penarikan tidak boleh melebihi saldo, dan setoran harus bernilai positif. Encapsulation adalah praktik membundulkan data bersama method-method yang menjaga aturannya, lalu membatasi akses langsung dari luar. Untuk itu kita sementara berganti domain ke Rekening — kasus uang memang contoh terbaik encapsulation. Bahasa lain mengunci data dengan kata kunci private; Python memilih jalan berbeda, dan chapter ini menjelaskan jalannya.
Masalah Attribute Publik #
Secara default, semua attribute di Python bersifat publik — siapa pun bisa membaca dan menulis dari luar class:
class Rekening:
def __init__(self, pemilik, saldo):
self.pemilik = pemilik
self.saldo = saldo
rekening = Rekening("Budi", 500000)
rekening.saldo = -1000000
print(rekening.saldo)Output yang Dihasilkan:
-1000000Tidak ada peringatan, tidak ada validasi — aturan bisnis “saldo tidak boleh negatif” remuk dalam satu baris. Masalahnya bukan hilangnya tembok pengaman, melainkan tidak adanya titik tunggal pengendalian: kode mana pun di program bisa mengubah saldo lewat jalan pintas.
Konvensi _protected dan Method Penjaga #
Python memakai konvensi: attribute yang diawali satu underscore (_saldo) dibaca sebagai “internal — jangan disentuh dari luar”. Konvensi ini tidak ditegakkan interpreter, jadi kekuatannya ada di kesepakatan sosial. Class berikut memindahkan semua perubahan saldo ke method yang bisa memeriksa aturan dulu:
class Rekening:
def __init__(self, pemilik, saldo_awal):
self.pemilik = pemilik
self._saldo = saldo_awal
def setor(self, jumlah):
if jumlah <= 0:
print("Jumlah setor harus lebih dari 0")
return
self._saldo += jumlah
def tarik(self, jumlah):
if jumlah > self._saldo:
print("Saldo tidak cukup")
return
self._saldo -= jumlah
rekening = Rekening("Budi", 500000)
rekening.setor(250000)
rekening.tarik(100000)
rekening.tarik(999999999)
print("Saldo akhir:", rekening._saldo)Output yang Dihasilkan:
Saldo tidak cukup
Saldo akhir: 650000Mari kita telaah hasilnya:
- Baris 4 (
self._saldo = saldo_awal): satu underscore di depan memberi sinyal “internal” kepada pembaca kode. - Method
setor()dantarik(): keduanya memvalidasi permintaan sebelum menyentuh_saldo. Penarikan sebesar999999999ditolak dengan pesan, dan saldo tetap650000(500000 + 250000 − 100000). - Baris terakhir (
rekening._saldo): akses langsung seperti ini tetap bisa — interpreter tidak melarang. Konvensi hanya sinyal, bukan tembok.
Filosofi ini sering diringkas sebagai consenting adults: Python percaya pembaca kode adalah orang dewasa yang bisa menghormati konvensi. Library yang mengandalkan _attribute internal memberi tahu kita “boleh saja memaksa, tapi jangan salahkan penulis library jika versi berikutnya berubah”.
Name Mangling dengan __saldo #
Dua underscore di depan (__saldo) memicu mekanisme berbeda: name mangling. Interpreter menulis ulang nama attribute tersebut menjadi _NamaClass__saldo pada saat class didefinisikan:
class Rekening:
def __init__(self, pemilik, saldo_awal):
self.pemilik = pemilik
self.__saldo = saldo_awal
def lihat_saldo(self):
return self.__saldo
rekening = Rekening("Siti", 1000000)
print(rekening.lihat_saldo())
print(rekening.__dict__)Output yang Dihasilkan:
1000000
{'pemilik': 'Siti', '_Rekening__saldo': 1000000}Kamus __dict__ memperlihatkan kebenarannya: attribute yang tersimpan bernama _Rekening__saldo, bukan __saldo. Akibatnya, akses dengan nama aslinya dari luar class gagal:
class Rekening:
def __init__(self, pemilik, saldo_awal):
self.pemilik = pemilik
self.__saldo = saldo_awal
def lihat_saldo(self):
return self.__saldo
rekening = Rekening("Siti", 1000000)
print(rekening.__saldo)Output yang Dihasilkan:
Traceback (most recent call last):
File "07d-error-saldo.py", line 11, in <module>
print(rekening.__saldo)
^^^^^^^^^^^^^^^^
AttributeError: 'Rekening' object has no attribute '__saldo'Interpreter mencari nama __saldo di kamus instance, tidak menemukannya (yang tersimpan adalah bentuk mangled), lalu memunculkan AttributeError. Nama file pada baris File "..." bisa berbeda di komputer lain — bentuk pesannya tetap sama.
Bentuk _Rekening__saldo tetap bisa diakses kalau kita benar-benar menuliskannya — jadi ini juga bukan pengamanan sungguhan. Tujuan utama name mangling adalah mencegah akses tak sengaja dan mencegah tabrakan nama attribute antara class induk dan anak saat kita sampai ke inheritance.
Pratinjau property #
Begitu data bergerak ke balik method penjaga, membaca rekening.saldo() terasa kaku — padahal membaca data. Decorator @property menjembatani keduanya: pemanggil menulis seperti membaca attribute, interpreter menjalankan sebuah method:
class Rekening:
def __init__(self, pemilik, saldo_awal):
self.pemilik = pemilik
self._saldo = saldo_awal
@property
def saldo(self):
return self._saldo
rekening = Rekening("Budi", 500000)
print(rekening.saldo)Output yang Dihasilkan:
500000Perhatikan baris terakhir: rekening.saldo tanpa tanda kurung, namun method tetap berjalan. Kombinasi _saldo + @property memberi akses baca yang rapi sambil menjaga titik tunggal pengendalian — suatu hari validasi bisa ditambahkan tanpa mengubah kode pemanggil. Pembahasan penuh property (termasuk setter dan validasi tulis) menyusul pada chapter Properties di Section Advanced Data Modeling.