Apa cara pythonic untuk menggunakan getter dan setter?
Cara "Pythonic" bukanlah menggunakan "getter" dan "setters", tetapi menggunakan atribut polos, seperti yang diperlihatkan oleh pertanyaan, dan del
untuk menghapus (tetapi namanya diubah untuk melindungi yang tidak bersalah ... bawaan):
value = 'something'
obj.attribute = value
value = obj.attribute
del obj.attribute
Jika nanti, Anda ingin mengubah pengaturan dan mendapatkan, Anda dapat melakukannya tanpa harus mengubah kode pengguna, dengan menggunakan property
dekorator:
class Obj:
"""property demo"""
#
@property # first decorate the getter method
def attribute(self): # This getter method name is *the* name
return self._attribute
#
@attribute.setter # the property decorates with `.setter` now
def attribute(self, value): # name, e.g. "attribute", is the same
self._attribute = value # the "value" name isn't special
#
@attribute.deleter # decorate with `.deleter`
def attribute(self): # again, the method name is the same
del self._attribute
(Setiap dekorator menggunakan salinan dan memperbarui objek properti sebelumnya, jadi perhatikan bahwa Anda harus menggunakan nama yang sama untuk setiap set, mendapatkan, dan menghapus fungsi / metode.
Setelah mendefinisikan di atas, pengaturan asli, mendapatkan, dan menghapus kode adalah sama:
obj = Obj()
obj.attribute = value
the_value = obj.attribute
del obj.attribute
Anda harus menghindari ini:
def set_property(property,value):
def get_property(property):
Pertama, hal di atas tidak berfungsi, karena Anda tidak memberikan argumen untuk contoh bahwa properti akan diatur ke (biasanya self
), yang akan menjadi:
class Obj:
def set_property(self, property, value): # don't do this
...
def get_property(self, property): # don't do this either
...
Kedua, ini menggandakan tujuan dari dua metode khusus, __setattr__
dan __getattr__
.
Ketiga, kami juga memiliki fungsi setattr
dan getattr
builtin.
setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value) # default is optional
The @property
dekorator adalah untuk menciptakan getter dan setter.
Misalnya, kita bisa mengubah perilaku pengaturan untuk menempatkan batasan nilai yang ditetapkan:
class Protective(object):
@property
def protected_value(self):
return self._protected_value
@protected_value.setter
def protected_value(self, value):
if acceptable(value): # e.g. type or range check
self._protected_value = value
Secara umum, kami ingin menghindari menggunakan property
dan hanya menggunakan atribut langsung.
Inilah yang diharapkan oleh pengguna Python. Mengikuti aturan yang paling tidak mengejutkan, Anda harus mencoba memberikan apa yang mereka harapkan kepada pengguna kecuali Anda memiliki alasan yang sangat kuat untuk melakukan hal sebaliknya.
Demonstrasi
Misalnya, kami membutuhkan atribut objek yang kami lindungi untuk menjadi bilangan bulat antara 0 dan 100 inklusif, dan mencegah penghapusannya, dengan pesan yang sesuai untuk memberi tahu pengguna tentang penggunaannya yang benar:
class Protective(object):
"""protected property demo"""
#
def __init__(self, start_protected_value=0):
self.protected_value = start_protected_value
#
@property
def protected_value(self):
return self._protected_value
#
@protected_value.setter
def protected_value(self, value):
if value != int(value):
raise TypeError("protected_value must be an integer")
if 0 <= value <= 100:
self._protected_value = int(value)
else:
raise ValueError("protected_value must be " +
"between 0 and 100 inclusive")
#
@protected_value.deleter
def protected_value(self):
raise AttributeError("do not delete, protected_value can be set to 0")
(Catatan yang __init__
mengacu pada self.protected_value
tetapi metode properti merujuk self._protected_value
. Ini adalah agar __init__
menggunakan properti melalui API publik, memastikan itu "dilindungi".)
Dan penggunaan:
>>> p1 = Protective(3)
>>> p1.protected_value
3
>>> p1 = Protective(5.0)
>>> p1.protected_value
5
>>> p2 = Protective(-5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> p1.protected_value = 7.3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 17, in protected_value
TypeError: protected_value must be an integer
>>> p1.protected_value = 101
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> del p1.protected_value
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 18, in protected_value
AttributeError: do not delete, protected_value can be set to 0
Apakah nama itu penting?
Ya mereka lakukan . .setter
dan .deleter
membuat salinan dari properti asli. Ini memungkinkan subclass untuk memodifikasi perilaku dengan benar tanpa mengubah perilaku pada induknya.
class Obj:
"""property demo"""
#
@property
def get_only(self):
return self._attribute
#
@get_only.setter
def get_or_set(self, value):
self._attribute = value
#
@get_or_set.deleter
def get_set_or_delete(self):
del self._attribute
Agar ini berfungsi, Anda harus menggunakan nama masing-masing:
obj = Obj()
# obj.get_only = 'value' # would error
obj.get_or_set = 'value'
obj.get_set_or_delete = 'new value'
the_value = obj.get_only
del obj.get_set_or_delete
# del obj.get_or_set # would error
Saya tidak yakin di mana ini akan berguna, tetapi kasus penggunaannya adalah jika Anda menginginkan properti get, set, dan / atau delete-only. Mungkin lebih baik tetap pada properti semantik yang sama dengan nama yang sama.
Kesimpulan
Mulai dengan atribut sederhana.
Jika nanti Anda membutuhkan fungsionalitas di sekitar pengaturan, mendapatkan, dan menghapus, Anda dapat menambahkannya dengan dekorator properti.
Hindari fungsi bernama set_...
dan get_...
- itulah gunanya properti.