Alıcıları ve ayarlayıcıları kullanmanın pitonik yolu nedir?
"Pythonic" yoludur değil "alıcılar" ve "belirleyiciler" kullanmak, ama soru gösterir gibi, düz özelliklerini kullanmak ve del
silme (ancak isimler masum ... yerleşiklerini korumak için değiştirilir):
value = 'something'
obj.attribute = value
value = obj.attribute
del obj.attribute
Daha sonra, ayarı değiştirmek ve almak istiyorsanız, bunu property
dekoratör kullanarak kullanıcı kodunu değiştirmek zorunda kalmadan yapabilirsiniz :
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
(Her dekoratör kullanımı önceki özellik nesnesini kopyalar ve günceller, bu nedenle her küme, alma ve silme işlevi / yöntemi için aynı adı kullanmanız gerektiğini unutmayın.
Yukarıdakileri tanımladıktan sonra, orijinal ayar, alma ve kod silme aynıdır:
obj = Obj()
obj.attribute = value
the_value = obj.attribute
del obj.attribute
Bundan kaçınmalısınız:
def set_property(property,value):
def get_property(property):
Birincisi, yukarıdakiler işe yaramaz, çünkü özelliğin (genellikle self
) olarak ayarlanacağı örnek için bir argüman sağlamazsınız ;
class Obj:
def set_property(self, property, value): # don't do this
...
def get_property(self, property): # don't do this either
...
İkincisi, bu iki özel yöntemin amacını çoğaltır __setattr__
ve __getattr__
.
Üçüncüsü, setattr
ve getattr
yerleşik fonksiyonlara da sahibiz .
setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value) # default is optional
@property
Dekoratör alıcılar ve ayarlayıcılar oluşturmak içindir.
Örneğin, ayarlanan değeri kısıtlamalar koymak için ayar davranışını değiştirebiliriz:
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
Genel olarak, property
doğrudan özellikleri kullanmaktan kaçınmak ve kullanmak istiyoruz .
Python kullanıcıları tarafından beklenen budur. En az sürpriz kuralına uyarak, aksine çok zorlayıcı bir nedeniniz yoksa kullanıcılarınıza bekledikleri şeyi vermeye çalışmalısınız.
gösteri
Örneğin, nesnemizin korumalı özelliğinin 0 ile 100 arasında bir tamsayı olması ve silinmesini önlemek için kullanıcıyı uygun kullanımını bildiren uygun mesajlarla ihtiyacımız olduğunu varsayalım:
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")
(Not bu __init__
belirtmektedir self.protected_value
ama özelliği yöntemleri bakınız self._protected_value
. Bu şekilde bir __init__
kullanım da temin ortak API yoluyla maddi "korumalı").
Ve kullanım:
>>> 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
İsimler önemli mi?
Evet biliyorlar . .setter
ve .deleter
orijinal mülkün kopyalarını oluşturun. Bu, alt sınıfların üst öğedeki davranışı değiştirmeden davranışı düzgün bir şekilde değiştirmesini sağlar.
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
Şimdi bunun çalışması için ilgili isimleri kullanmalısınız:
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
Bunun yararlı olacağından emin değilim, ancak get, set ve / veya salt silme özelliği istiyorsanız use-case. Muhtemelen aynı ada sahip semantik olarak aynı mülke bağlı kalmak en iyisidir.
Sonuç
Basit özelliklerle başlayın.
Daha sonra ayar, alma ve silme etrafında işlevselliğe ihtiyacınız varsa, özelliği dekoratörle ekleyebilirsiniz.
set_...
Ve get_...
- adlı fonksiyonlardan kaçının .