Python @property Dekoratörü
Bu eğitimde Python @property dekoratörü hakkında bilgi edineceksiniz; nesne yönelimli programlamada alıcıları ve ayarlayıcıları kullanmanın Pythonic yolu.
@property
Python programlama bize Nesne Yönelimli Programlamada alıcı ve ayarlayıcıların kullanımını çok daha kolay hale getiren yerleşik bir dekoratör sağlar.
Dekoratörün ne olduğuna dair ayrıntılara girmeden önce @property
, ilk etapta neden gerekli olduğuna dair bir sezgi oluşturalım.
Getters ve Setters olmadan Sınıf
Sıcaklığı Santigrat derece cinsinden saklayan bir sınıf yapmaya karar verdiğimizi varsayalım . Ayrıca sıcaklığı Fahrenheit derecesine dönüştürmek için bir yöntem uygulayacaktır. Bunu yapmanın bir yolu aşağıdaki gibidir:
1 2 3 4 5 6 | class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 |
Bu sınıftan nesneler yapabilir ve temperature
özniteliği istediğimiz gibi değiştirebiliriz:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # 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()) |
Çıktı
1 2 | <samp>37 98.600000000000001</samp> |
Fahrenheit’e dönüştürürken fazladan ondalık basamaklar, kayan nokta aritmetik hatasından kaynaklanır. Daha fazla bilgi edinmek için Python Kayan Nokta Aritmetik Hatası sayfasını ziyaret edin .
Yukarıda gösterildiği gibi herhangi bir nesne özniteliği atadığımızda veya aldığımızda temperature
, Python onu nesnenin yerleşik __dict__
sözlük özniteliğinde arar.
1 2 | >>> human.__dict__ {'temperature': 37} |
Bu nedenle, man.temperature
dahili olur man.__dict__['temperature']
.
Alıcıları ve Ayarlayıcıları Kullanma
Kullanılabilirliğini genişletmek istediğimizi varsayalım.santigratYukarıda tanımlanan sınıf. Hiçbir cismin sıcaklığının -273,15 santigrat derecenin altına ulaşamayacağını biliyoruz (Termodinamikte Mutlak Sıfır)
Bu değer kısıtlamasını uygulamak için kodumuzu güncelleyelim.
Yukarıdaki kısıtlamanın bariz bir çözümü, niteliği gizlemek temperature
(özel yapmak) ve onu manipüle etmek için yeni alıcı ve ayarlayıcı yöntemleri tanımlamak olacaktır. Bu şöyle yapılabilir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 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 |
Gördüğümüz gibi, yukarıdaki yöntem iki yeni get_temperature()
ve yöntemi tanıtıyor set_temperature()
.
Ayrıca, temperature
ile değiştirildi _temperature
. _
Python’da özel değişkenleri belirtmek için başlangıçta bir alt çizgi kullanılır.
Şimdi bu uygulamayı kullanalım:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | # 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()) |
Çıktı
1 2 3 4 5 6 | <samp>37 98.600000000000001 Geri izleme (en son arama son): Dosya "<string>", satır 30, <module> içinde "<string>" dosyası, 16. satır, set_temperature'da ValueError: -273.15'in altındaki sıcaklık mümkün değil.</samp> |
Bu güncelleme, yeni kısıtlamayı başarıyla uyguladı. Artık sıcaklığı -273,15 santigrat derecenin altına ayarlamamıza izin verilmemektedir.
Not : Özel değişkenler aslında Python’da mevcut değildir. Sadece uyulması gereken normlar vardır. Dilin kendisi herhangi bir kısıtlama uygulamaz.
1 2 3 | >>> human._temperature = -300 >>> human.get_temperature() -300 |
Ancak, yukarıdaki güncellemeyle ilgili daha büyük sorun, önceki sınıfımızı uygulayan tüm programların kodlarını obj.temperature
to ile obj.get_temperature()
ve gibi tüm ifadeleri değiştirmesi obj.temperature = val
gerektiğidir obj.set_temperature(val)
.
Bu yeniden düzenleme, yüz binlerce kod satırıyla uğraşırken sorunlara neden olabilir.
Sonuç olarak, yeni güncellememiz geriye dönük uyumlu değildi. Burası @property
kurtarmaya geldiği yer.
Mülkiyet Sınıfı
Yukarıdaki problemle başa çıkmanın Pythonic bir yolu, property
sınıfı kullanmaktır. Kodumuzu şu şekilde güncelleyebiliriz:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # 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) |
print()
İçeriye bir fonksiyon ekledik get_temperature()
ve set_temperature()
yürütüldüklerini açıkça gözlemlemek için.
Kodun son satırı bir özellik nesnesi yapar temperature
. Basitçe söylemek gerekirse, özellik erişim ( ) üye özniteliğine bazı kodlar ( get_temperature
ve ) ekler .set_temperature
temperature
Bu güncelleme kodunu kullanalım:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # 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 |
Çıktı
1 2 3 4 5 6 7 8 9 10 | <samp>Ayar değeri... Değer elde etmek... 37 Değer elde etmek... 98.600000000000001 Ayar değeri... Geri izleme (en son arama son): Dosya "<string>", satır 31, <module> içinde "<string>" dosyası, 18. satır, set_temperature'da ValueError: -273'ün altındaki sıcaklık mümkün değil</samp> |
Gördüğümüz gibi, değerini alan herhangi bir kod, bir sözlük (__dict__) temperature
araması yerine otomatik olarak arayacaktır . get_temperature()
Benzer şekilde, bir değer atayan herhangi bir kod temperature
otomatik olarak set_temperature()
.
Hatta yukarıda set_temperature()
bir nesne oluşturduğumuzda bile çağrıldığını görebiliriz.
1 2 | >>> human = Celsius(37) Setting value... |
Neden olduğunu tahmin edebilir misin?
Bunun nedeni, bir nesne oluşturulduğunda __init__()
yöntemin çağrılmasıdır. Bu yöntemin çizgisi vardır self.temperature = temperature
. Bu ifade otomatik olarak set_temperature()
.
c.temperature
Benzer şekilde, otomatik aramalar gibi herhangi bir erişim get_temperature()
. Mülkiyetin yaptığı budur. İşte birkaç örnek daha.
1 2 3 4 5 6 7 8 9 | >>> human.temperature Getting value 37 >>> human.temperature = 37 Setting value >>> c.to_fahrenheit() Getting value 98.60000000000001 |
kullanarak property
, değer kısıtlamasının uygulanmasında herhangi bir değişiklik yapılmasına gerek olmadığını görebiliriz. Bu nedenle, uygulamamız geriye dönük olarak uyumludur.
Not : Gerçek sıcaklık değeri özel _temperature
değişkende saklanır. Nitelik temperature
, bu özel değişkene bir arayüz sağlayan bir özellik nesnesidir.
@property Dekoratörü
Python’da, bir nesne property()
oluşturan ve döndüren yerleşik bir işlevdir . property
Bu işlevin sözdizimi şöyledir:
1 | property(fget=None, fset=None, fdel=None, doc=None) |
nerede,
fget
özelliğin değerini alma işlevidirfset
özelliğin değerini ayarlama işlevidirfdel
özniteliği silme işlevidirdoc
bir dizedir (bir yorum gibi)
Uygulamadan görüldüğü gibi, bu fonksiyon argümanları isteğe bağlıdır. Böylece, bir özellik nesnesi basitçe aşağıdaki gibi oluşturulabilir.
1 2 | >>> property() <property object at 0x0000000003239B38> |
getter()
Bir özellik nesnesinin, , setter()
, ve deleter()
belirtmek için fget
ve fset
daha fdel
sonraki bir noktada olmak üzere üç yöntemi vardır . Bu, şu satır anlamına gelir:
1 | temperature = property(get_temperature,set_temperature) |
şu şekilde parçalanabilir:
1 2 3 4 5 6 | # make empty property temperature = property() # assign fget temperature = temperature.getter(get_temperature) # assign fset temperature = temperature.setter(set_temperature) |
Bu iki kod parçası eşdeğerdir.
Python Dekoratörlerine aşina olan programcılar , yukarıdaki yapının dekoratörler olarak uygulanabileceğini fark edebilir.
İsimleri tanımlayamıyoruz get_temperature
ve set_temperature
gereksiz oldukları için sınıf isim alanını kirletiyoruz.
temperature
Bunun için getter ve setter fonksiyonlarımızı tanımlarken ismi tekrar kullanıyoruz. Bunu bir dekoratör olarak nasıl uygulayacağımıza bakalım:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # 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) |
Çıktı
1 2 3 4 5 6 7 8 9 10 11 | <samp>Ayar değeri... Değer elde etmek... 37 Değer elde etmek... 98.600000000000001 Ayar değeri... Geri izleme (en son arama son): Dosya "", satır 29, içinde Dosya "", satır 4, __init__ içinde Dosya "", satır 18, sıcaklıkta ValueError: -273'ün altındaki sıcaklık mümkün değil</samp> |
Yukarıdaki uygulama basit ve verimlidir. Tavsiye edilen kullanım şeklidir property
.