Programlama dillerinde mutator yöntemi bir değişkendeki değişiklikleri kontrol etmek için kullanılan bir yöntemdir. Genel olarak getter/setter yöntemleri olarak da bilinirler.
Mutator yöntemi en sık kapsülleme ilkesine uygun olarak nesne yönelimli programlamada kullanılır. Bu ilkeye göre, bir sınıfın üye değişkenleri, bunları gizlemek ve diğer kodlardan korumak için özel hale getirilirler. Dolayısıyla yalnızca istenen yeni değeri parametre olarak alan, isteğe bağlı olarak doğrulayan ve bu değişkeni değiştiren bir fonksiyon (mutator yöntemi) tarafından değiştirilebilirler.
Başka bir programla dilinden Python’a geçtiyseniz getter/setter tanımlamak için aşağıdakine benzer bir yöntem kullanıyor olabilirsiniz:
def set_property(property,value):
def get_property(property):
Ancak Pythonda bu sevilmez. Mutator kullanmanın “Pythonic” yolu “getters” ve “setters” kullanmak değildir. Bunun aksine python özelliklerini (property) kullanırız.
value
adında bir değişkenimiz olsun. Bu değişkenin değerine aşağıdaki gibi ulaşırız:
value = 'something'
obj.attribute = value #set
value = obj.attribute #get
del obj.attribute #del
Sonrasında bu değerleri değiştirmek veya almak için ise property
dekoratörünü kullanarak kullanıcı kodunu değiştirmek zorunda kalmadan değere ulaşabiliriz.
class Obj:
"""property demo"""
#
@property # getter metodu
def attribute(self): # getter metodu değişken ismi ile aynı
return self._attribute
#
@attribute.setter # .setter dekoratörü
def attribute(self, value): # fonksiyon ismi aynı
self._attribute = value # value değişkenine eşitledik
#
@attribute.deleter # silmek için .deleter dekoratörü
def attribute(self): # metod adı yine aynı
del self._attribute
(Her dekoratör kullanımı önceki özellik nesnesini kopyalar ve güncelleştirir, bu nedenle her getter/setter ve silme işlemi için aynın isimde işlev/yöntem kullanmanız gerektiğini unutmayın.)
Yukarıdakileri tanımladıktan sonra koddan erişimimiz ilk başta yazdığımız gibi olacak:
obj = Obj()
obj.attribute = value
the_value = obj.attribute
del obj.attribute
Tekrar başa dönelim ve ilk başta düşündüğümüz getter/setter metotlarımıza bakalım.
def set_property(property,value):
def get_property(property):
Öncelikle, yukarıdakiler çalışmayacaktır. Çünkü değerin eşitleneceğini belirteceğimiz bağımsız bir değişken sağlamıyoruz – bu genellikle self ile tanımlanır.
class Obj:
def set_property(self, property, value):
...
def get_property(self, property):
...
İkincisi, bu kullanım tarzı __setattr__
ve __getattr__
yöntemlerinin sadece fazladan bir kopyasını oluşturuyor.
Üçüncü olarak, pythonda yerleşik setattr
ve getattr
fonksiyonlarına sahibiz.
setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value)
@property
dekoratörü, getters ve setterler yaratmak içindir.
Öte yandan değerlerin erişimini kısıtlamak için fonksiyonun 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
Sonuç olarak, doğrudan öznitelikleri kullanmaktan kaçınmak ve sadece property
kullanmak isteriz.
Diğer Python kullanıcıları tarafından da beklenen budur. “En az sürpriz” kuralına uyarız. Zorlayıcı bir nedeniniz olmadıkça kodunuzu okuyacak kişilere beklediklerini vermeye çalışmalısınız.
Örnek
Örneğin, nesnemizin korumalı özniteliğinin 0 ile 100 (dahil) arasında bir tamsayı olmasını isteyelim. Kullanıcıya da bu kuralların uygun kullanımı hakkında bilgilendireceğimiz mesajlarımız olsun. Ayrıca özelliğin silinmesini de önlemek isteyelim:
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")
Kullanıma bakalı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
İsimlerin önemi var mı?
Evet, var. .setter
ve .deleter
orijinal özelliğin kopyalarını yapar. Bu, alt sınıfların üstteki davranışı değiştirmeden kendi davranışını düzgün bir şekilde değiştirmesine olanak tanır.
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 ilgili adları kullanmanız gerekir:
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
Son
Basit özelliklerle başlayın. Daha sonra get, set ve silme işlevlerine ihtiyacınız varsa dekoratör aracılığı ile bunları ekleyin. Bunu yaparken get_
veya set_
isimli işlevleri kullanmaktan kaçını.