Kursus Python / Mengatur perubahan objek dengan metode dan property

Metode menyatakan operasi objek, sedangkan property mempertahankan sintaks akses atribut sambil menerapkan aturan pembacaan atau penulisan. Dengan property, nilai awal dan perubahan berikutnya dapat melewati pemeriksaan yang sama.

Pisahkan antarmuka publik dari tempat penyimpanan

Antarmuka publik contoh ini bernama amount, sedangkan nilai disimpan pada atribut internal _amount. Awalan garis bawah memberi petunjuk bahwa atribut itu merupakan rincian implementasi. Ia tidak melarang kode luar mengaksesnya dan bukan pengamanan untuk menyimpan rahasia.

Membaca item.amount memanggil getter. Memberi nilai melalui item.amount = value memanggil setter. Pemanggil memakai bentuk atribut tanpa tanda kurung; ini berbeda dari operasi seperti item.add_fee(500). Dasar pembuatannya ada pada dokumentasi property Python.

Pada contoh class dan initializer, pemeriksaan saat pembuatan saja belum mengendalikan perubahan kemudian. Di sini, __init__ menggunakan self.amount = amount sehingga nilai awal ikut melewati setter. Menulis langsung ke _amount di initializer akan melewati aturan property tersebut.

Jalankan perubahan yang valid dan yang ditolak

Simpan program lengkap berikut sebagai properties.py dengan encoding UTF-8. Gunakan Python 3.12 atau lebih baru melalui lingkungan proyek, lalu jalankan python properties.py. Contoh tidak memerlukan paket atau berkas data tambahan.

class Expense:
    def __init__(self, amount):
        self.amount = amount

    @property
    def amount(self):
        return self._amount

    @amount.setter
    def amount(self, value):
        if type(value) is not int or value < 0:
            raise ValueError("amount must be a nonnegative integer")
        self._amount = value

    def add_fee(self, fee):
        self.amount = self.amount + fee

item = Expense(8000)
item.add_fee(500)
print(item.amount)
try:
    item.amount = -1
except ValueError:
    print("rejected")
print(item.amount)

Keluaran yang diharapkan:

8500
rejected
8500

Pembuatan objek menyimpan 8000 melalui setter. Metode add_fee membaca jumlah, menghitung 8500, lalu memberikan hasil melalui setter yang sama. Ketika -1 ditetapkan, pemeriksaan menaikkan ValueError sebelum penyimpanan, sehingga jumlah lama tetap 8500 setelah penolakan.

type(value) is not int menyatakan kebijakan menerima integer biasa saja. Dengan aturan tersebut, True ditolak sebagai nilai jumlah walaupun boolean merupakan subclass integer, begitu juga 3.5 atau string angka. Ini keputusan antarmuka contoh, bukan aturan wajib untuk setiap property.

Bentuk akses Perilaku pada contoh
item.amount Getter membaca _amount
item.amount = 100 Setter memvalidasi lalu menyimpan
item.add_fee(500) Metode menghitung perubahan dan memakai setter

Hindari rekursi dan perubahan sebagian

Setter harus menyimpan hasil ke atribut pendukung seperti self._amount. Jika baris terakhir diganti menjadi self.amount = value, setter memanggil dirinya lagi. Pemanggilan itu berulang sampai gagal dengan RecursionError. Getter juga harus membaca atribut pendukung, bukan mengakses property yang sama tanpa jalan keluar.

Lakukan validasi sebelum mengubah keadaan. Jika nilai salah disimpan terlebih dahulu baru pengecualian dinaikkan, pemanggil mendapat kegagalan tetapi objek mungkin sudah tidak valid. Uji keduanya: jenis kegagalan dan keadaan yang tersisa setelah kegagalan.

Jangan menyembunyikan permintaan jaringan atau pembacaan berkas besar di balik akses yang tampak sebagai pembacaan atribut sederhana. Metode bernama jelas biasanya lebih mudah menyampaikan bahwa ada pekerjaan mahal, efek samping, atau kemungkinan gagal.

Property tanpa setter menolak pemberian nilai biasa melalui property tersebut. Namun atribut pendukung atau bagian lain objek masih dapat berubah. Antarmuka baca-saja tidak membuat seluruh objek menjadi immutable secara mendalam, dan konvensi garis bawah tidak membatasi kode luar secara keamanan.

Uji aturan jumlah dan aturan biaya secara terpisah

  1. Jalankan sumber asli dan cocokkan 8500, rejected, lalu 8500 lagi. Nilai terakhir memeriksa bahwa pembaruan gagal tidak merusak keadaan.
  2. Coba menetapkan True dan 3.5 pada amount secara terpisah dengan penangan ValueError. Setelah masing-masing penolakan, pastikan nilai tetap 8500.
  3. Coba add_fee(-500) dari keadaan 8500. Contoh mengizinkan hasil 8000. Sebaliknya, biaya -9000 membuat hasil negatif dan ditolak tanpa mengganti jumlah sebelumnya.
  4. Pulihkan program asli setelah percobaan dan cocokkan kembali keluarannya sebelum memperluas aturan.

Validasi hasil jumlah tidak otomatis memvalidasi arti biaya. Misalnya, add_fee(True) dapat menambah 1 karena operasi penjumlahan menghasilkan integer yang memenuhi setter. Jika biaya harus integer biasa dan nonnegatif, metode perlu memeriksa tipe serta rentang fee sendiri sebelum menghitung hasil.

Tentukan apakah biaya negatif memang berarti potongan yang sah atau masukan yang harus ditolak. Pertahankan aturan jumlah dan aturan operasi sebagai dua kontrak yang eksplisit. Property membantu mengarahkan akses, tetapi konsistensi rancangan dan pengujian tetap menentukan nilai apa yang diterima.