Python @property: Bagaimana Cara Menggunakannya dan Mengapa? - Programiz

Dalam tutorial ini, Anda akan belajar tentang Python @property decorator; cara pythonic untuk menggunakan getter dan setter dalam pemrograman berorientasi objek.

Pemrograman Python memberi kita @propertydekorator built-in yang membuat penggunaan getter dan setter jauh lebih mudah dalam Pemrograman Berorientasi Objek.

Sebelum membahas secara detail tentang apa itu @propertydekorator, pertama-tama mari kita membangun intuisi tentang mengapa dekorator itu dibutuhkan.

Kelas Tanpa Getters dan Setter

Mari kita asumsikan bahwa kita memutuskan untuk membuat kelas yang menyimpan suhu dalam derajat Celcius. Itu juga akan menerapkan metode untuk mengubah suhu menjadi derajat Fahrenheit. Salah satu cara untuk melakukannya adalah sebagai berikut:

 class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32

Kita dapat membuat objek dari kelas ini dan memanipulasi temperatureatributnya sesuai keinginan:

 # Basic method of setting and getting attributes in Python class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # Create a new object human = Celsius() # Set the temperature human.temperature = 37 # Get the temperature attribute print(human.temperature) # Get the to_fahrenheit method print(human.to_fahrenheit())

Keluaran

 37 98.60000000000001

Tempat desimal tambahan saat mengonversi menjadi Fahrenheit disebabkan oleh kesalahan aritmatika titik mengambang. Untuk mempelajari lebih lanjut, kunjungi Kesalahan Aritmatika Titik Mengambang Python.

Setiap kali kita menetapkan atau mengambil atribut objek seperti yang temperatureditunjukkan di atas, Python mencarinya di __dict__atribut kamus bawaan objek.

 >>> human.__dict__ ('temperature': 37)

Oleh karena itu, secara man.temperatureinternal menjadi man.__dict__('temperature').

Menggunakan Getters dan Setter

Misalkan kita ingin memperluas kegunaan kelas Celsius yang ditentukan di atas. Kita tahu bahwa suhu suatu benda tidak dapat mencapai di bawah -273,15 derajat Celcius (Nol Mutlak dalam Termodinamika)

Mari perbarui kode kita untuk menerapkan batasan nilai ini.

Solusi yang jelas untuk pembatasan di atas adalah menyembunyikan atribut temperature(menjadikannya pribadi) dan menentukan metode pengambil dan penyetel baru untuk memanipulasinya. Hal ini dapat dilakukan sebagai berikut:

 # Making Getters and Setter methods class Celsius: def __init__(self, temperature=0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # getter method def get_temperature(self): return self._temperature # setter method def set_temperature(self, value): if value < -273.15: raise ValueError("Temperature below -273.15 is not possible.") self._temperature = value

Seperti yang bisa kita lihat, metode di atas memperkenalkan dua metode get_temperature()dan baru set_temperature().

Selanjutnya temperaturediganti dengan _temperature. Garis bawah _di awal digunakan untuk menunjukkan variabel privat dengan Python.

Sekarang, mari gunakan implementasi ini:

 # Making Getters and Setter methods class Celsius: def __init__(self, temperature=0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # getter method def get_temperature(self): return self._temperature # setter method def set_temperature(self, value): if value < -273.15: raise ValueError("Temperature below -273.15 is not possible.") self._temperature = value # Create a new object, set_temperature() internally called by __init__ human = Celsius(37) # Get the temperature attribute via a getter print(human.get_temperature()) # Get the to_fahrenheit method, get_temperature() called by the method itself print(human.to_fahrenheit()) # new constraint implementation human.set_temperature(-300) # Get the to_fahreheit method print(human.to_fahrenheit())

Keluaran

 37 98.60000000000001 Penelusuran balik (panggilan terakhir terakhir): File "", baris 30, di File "", baris 16, di set_temperature ValueError: Suhu di bawah -273.15 tidak mungkin.

Pembaruan ini berhasil menerapkan batasan baru. Kami tidak lagi diperbolehkan mengatur suhu di bawah -273,15 derajat Celcius.

Catatan : Variabel privat sebenarnya tidak ada di Python. Ada norma sederhana yang harus diikuti. Bahasanya sendiri tidak menerapkan batasan apa pun.

 >>> human._temperature = -300 >>> human.get_temperature() -300

Namun, masalah yang lebih besar dengan update di atas adalah bahwa semua program yang dilaksanakan kelas kami sebelumnya harus memodifikasi kode mereka dari obj.temperatureke obj.get_temperature()dan semua ekspresi seperti obj.temperature = valuntuk obj.set_temperature(val).

Pemfaktoran ulang ini dapat menyebabkan masalah saat menangani ratusan ribu baris kode.

Secara keseluruhan, pembaruan baru kami tidak kompatibel dengan versi sebelumnya. Di sinilah @propertydatang untuk menyelamatkan.

Kelas properti

Cara pythonic untuk mengatasi masalah di atas adalah dengan menggunakan propertykelas. Inilah cara kami memperbarui kode kami:

 # using property class class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # getter def get_temperature(self): print("Getting value… ") return self._temperature # setter def set_temperature(self, value): print("Setting value… ") if value < -273.15: raise ValueError("Temperature below -273.15 is not possible") self._temperature = value # creating a property object temperature = property(get_temperature, set_temperature)

Kami menambahkan print()fungsi di dalam get_temperature()dan set_temperature()untuk mengamati dengan jelas bahwa mereka sedang dieksekusi.

Baris terakhir kode membuat objek properti temperature. Sederhananya, properti melampirkan beberapa kode ( get_temperaturedan set_temperature) ke atribut anggota accesses ( temperature).

Mari gunakan kode pembaruan ini:

 # using property class class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # getter def get_temperature(self): print("Getting value… ") return self._temperature # setter def set_temperature(self, value): print("Setting value… ") if value < -273.15: raise ValueError("Temperature below -273.15 is not possible") self._temperature = value # creating a property object temperature = property(get_temperature, set_temperature) human = Celsius(37) print(human.temperature) print(human.to_fahrenheit()) human.temperature = -300

Keluaran

 Menyetel nilai… Mendapatkan nilai… 37 Mendapatkan nilai… 98.60000000000001 Menyetel nilai… Melacak kembali (panggilan terakhir terakhir): File "", baris 31, di File "", baris 18, di set_temperature ValueError: Suhu di bawah -273 tidak mungkin

As we can see, any code that retrieves the value of temperature will automatically call get_temperature() instead of a dictionary (__dict__) look-up. Similarly, any code that assigns a value to temperature will automatically call set_temperature().

We can even see above that set_temperature() was called even when we created an object.

 >>> human = Celsius(37) Setting value… 

Can you guess why?

The reason is that when an object is created, the __init__() method gets called. This method has the line self.temperature = temperature. This expression automatically calls set_temperature().

Similarly, any access like c.temperature automatically calls get_temperature(). This is what property does. Here are a few more examples.

 >>> human.temperature Getting value 37 >>> human.temperature = 37 Setting value >>> c.to_fahrenheit() Getting value 98.60000000000001

By using property, we can see that no modification is required in the implementation of the value constraint. Thus, our implementation is backward compatible.

Note: The actual temperature value is stored in the private _temperature variable. The temperature attribute is a property object which provides an interface to this private variable.

The @property Decorator

In Python, property() is a built-in function that creates and returns a property object. The syntax of this function is:

 property(fget=None, fset=None, fdel=None, doc=None)

where,

  • fget is function to get value of the attribute
  • fset is function to set value of the attribute
  • fdel is function to delete the attribute
  • doc is a string (like a comment)

As seen from the implementation, these function arguments are optional. So, a property object can simply be created as follows.

 >>> property() 

A property object has three methods, getter(), setter(), and deleter() to specify fget, fset and fdel at a later point. This means, the line:

 temperature = property(get_temperature,set_temperature)

can be broken down as:

 # make empty property temperature = property() # assign fget temperature = temperature.getter(get_temperature) # assign fset temperature = temperature.setter(set_temperature)

Kedua potongan kode ini setara.

Pemrogram yang akrab dengan Python Decorators dapat mengenali bahwa konstruksi di atas dapat diimplementasikan sebagai dekorator.

Kami bahkan tidak dapat menentukan nama get_temperaturedan set_temperaturekarena tidak diperlukan dan mencemari namespace kelas.

Untuk ini, kami menggunakan kembali temperaturenama tersebut sambil mendefinisikan fungsi pengambil dan penyetel. Mari kita lihat bagaimana menerapkan ini sebagai dekorator:

 # Using @property decorator class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 @property def temperature(self): print("Getting value… ") return self._temperature @temperature.setter def temperature(self, value): print("Setting value… ") if value < -273.15: raise ValueError("Temperature below -273 is not possible") self._temperature = value # create an object human = Celsius(37) print(human.temperature) print(human.to_fahrenheit()) coldest_thing = Celsius(-300)

Keluaran

 Menyetel nilai… Mendapatkan nilai… 37 Mendapatkan nilai… 98.60000000000001 Menyetel nilai… Melacak kembali (panggilan terakhir terakhir): File "", baris 29, dalam File "", baris 4, dalam __init__ File "", baris 18, dalam suhu ValueError: Suhu di bawah -273 tidak memungkinkan

Implementasi di atas sederhana dan efisien. Ini adalah cara yang disarankan untuk digunakan property.

Artikel yang menarik...