Kursus Python / Membedakan atribut instance dan atribut class
Atribut instance menyimpan keadaan milik objek tertentu, sedangkan atribut class menyediakan nilai melalui class yang dapat dipakai bersama. Jika setiap objek membutuhkan list yang independen, buat list baru saat inisialisasi instance, bukan satu list pada badan class.
Cari lokasi atribut untuk memahami pembagian keadaan
Pemberian nilai self.items = [] di __init__ membuat atribut pada instance yang sedang diinisialisasi. Sebaliknya, items = [] di badan class membuat satu list milik class. Instance tanpa atribut items sendiri dapat mengakses list itu melalui pencarian atribut class.
Pada atribut biasa contoh ini, nilai milik instance dapat menutupi atau melakukan shadowing terhadap nama yang sama pada class. Perintah b.currency = "USD" tidak mengganti Ledger.currency; ia membuat atribut currency milik b. Perubahan objek mutable melalui append berbeda dari pemberian nilai baru pada atribut.
Hubungan nama dan objek mengikuti prinsip alias dan mutasi, sedangkan dasar instance dijelaskan pada pembuatan class dan metode. Untuk aturan resminya, lihat atribut class dan instance Python.
Jalankan ledger independen dan label yang diganti
Simpan program lengkap sebagai attributes.py dengan encoding UTF-8. Gunakan Python 3.12 atau lebih baru melalui lingkungan proyek dan jalankan python attributes.py. Tidak diperlukan paket atau data eksternal.
class Ledger:
currency = "KRW"
def __init__(self, owner):
self.owner = owner
self.items = []
def add(self, amount):
self.items.append(amount)
a = Ledger("A")
b = Ledger("B")
a.add(3500)
print(a.items, b.items)
b.currency = "USD"
print(a.currency, b.currency, Ledger.currency)
Keluaran yang diharapkan:
[3500] []
KRW USD KRW
Setiap pemanggilan Ledger menjalankan __init__ dan membuat list baru. Penambahan 3500 pada a tidak mengubah list b. Awalnya kedua objek dapat membaca label KRW melalui class. Setelah b.currency diberi USD, hanya b yang memakai label sendiri; nilai class tetap KRW.
Jika sesudahnya Anda menetapkan Ledger.currency = "EUR", a.currency membaca EUR karena belum mempunyai label sendiri. b.currency tetap USD. Menghapus atribut instance dengan del b.currency akan membuat pencarian kembali menemukan EUR pada class dalam contoh ini. Ini bukan konversi mata uang, melainkan perubahan label yang diakses.
Jika kebijakan label seperti itu membingungkan, mata uang dapat dijadikan argumen inisialisasi yang disimpan secara eksplisit per instance. Pilih desain berdasarkan apakah nilai memang perlu dibagi atau dapat berbeda pada tiap objek.
Bandingkan satu list bersama dengan list per instance
Simpan contoh kedua sebagai sharing_demo.py agar program ledger tetap tersedia. Jalankan python sharing_demo.py dalam folder yang sama. Program berikut berdiri sendiri.
class Shared:
items = []
class Separate:
def __init__(self):
self.items = []
a, b = Shared(), Shared()
a.items.append(1)
print(a.items, b.items)
x, y = Separate(), Separate()
x.items.append(1)
print(x.items, y.items)
Keluaran yang diharapkan:
[1] [1]
[1] []
Shared.items dibuat satu kali ketika badan class dieksekusi. Baik a.items maupun b.items menemukan list itu, sehingga satu penambahan melalui a terlihat melalui b. Pada Separate, masing-masing initializer membuat list baru; hanya list x yang berubah.
Menetapkan a.items = [] pada percobaan tambahan membuat atribut instance baru yang menutupi list class. List milik class dan list yang masih dibaca oleh b tidak otomatis dikosongkan. Karena itu, membandingkan operasi append dengan pemberian nilai atribut membantu menentukan objek mana yang sebenarnya diubah.
Jika initializer menerima list dari pemanggil dan menyimpannya langsung, list tersebut tetap dibagi dengan pemanggil. Menyalin dapat sesuai ketika kemandirian memang bagian kontraknya, tetapi salinan dangkal masih dapat membagi objek mutable di dalam. Memindahkan data ke instance saja bukan jaminan semua struktur bersarang menjadi independen.
Reproduksi pembagian yang keliru dan pulihkan desain
- Pada program ledger, pindahkan
items = []ke badan class dengan indentasi yang sama seperticurrency, lalu hapusself.items = []dari initializer. - Jalankan lagi. Baris pertama menjadi
[3500] [3500]karena kedua objek mengakses satu list class. Percobaan ini hanya mengubah lokasi penyimpanan, bukan isi metode penambahan. - Pulihkan
self.items = []di dalam__init__dan hapus list dari badan class. Jalankan proses baru, lalu cocokkan[3500] []. - Jalankan kembali program pembanding dan jelaskan terpisah identitas list bersama serta list yang independen.
Metode add pada contoh hanya menambahkan nilai; ia belum memeriksa tipe atau menolak angka negatif. Aturan validasi data tetap merupakan keputusan lain yang perlu dirancang dan diuji. Inisialisasi yang independen memperbaiki pembagian keadaan, bukan semua kemungkinan kesalahan masukan.
List bersama dapat berguna bila memang disengaja, tetapi masa hidup, kepemilikan, dan akses serentaknya harus ditentukan. Hindari memakai list class yang kebetulan dibagi sebagai penyimpanan global tidak resmi. Pengujian perlu mencakup lebih dari satu instance karena kesalahan pembagian sering tidak terlihat ketika hanya satu objek dibuat.




