Python, nesne yönelimli programlamayı çok daha kolay hale getiren property adında bir özelliğe sahiptir. Bu bölüm property konusuna ayrılmıştır.
Property konusuna başlamadan önce niye ihtiyaç olduğunu açıklayalım. Sıcaklığı santigrat cinsinden alan ve Fahrenhayt fonksiyonuna sahip olan bir sınıf tasarlayalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class santigrat: def __init__(self, sicaklik = 0): self.sicaklik = sicaklik def fahrenhayt(self): return (self.sicaklik * 1.8) + 32 n01 = santigrat(40) print(n01.sicaklik) print(n01.fahrenhayt()) n01.sicaklik = 80 print(n01.sicaklik) print(n01.fahrenhayt()) |
1 2 3 4 |
40 104.0 80 176.0 |
Yukarıdaki örnekte sicaklik self.sicaklik olduğu için nesne üzerinden ulaşılabilir. Hatta değiştirilebilir. fahrenhayt fonksiyonu santigrat cinsinden alınan değeri fahrenhayt türüne çevirir.
Ulaşabileceğiniz değişkenleri görmek için “n01.__dict__” ifadesi kullanılabilir. Çıktı olarak bir dictionary döndürür, örneğin bu örnek için {‘sicaklik’: 80} değeri döndürülür.
Aslında sicaklik değerinin -273’ten küçük olmayacağına dair bir bilgi edindik. Yukarıdaki sınıfa göre sicaklik değişkeni kontrol edilmektedir. Aslında bir sorun ile karşılaştık, değişkeni kontrol edemiyoruz. Çözüm olarak fonksiyonlar üzerinden kontrol edebiliriz ve değişkeni private duruma getirebiliriz. Ancak property sayesinde fonksiyona gerek kalmadan bir çözüm oluşturmaya çalışacağız.
15.1. Get ve set ile çözüm
Öncelikle problemi get ve set fonksiyonları ile çözelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class santigrat: def __init__(self, sicaklik = 0): self.set_sicaklik(sicaklik) def fahrenhayt(self): return (self.get_sicaklik() * 1.8) + 32 #get bölümü def get_sicaklik(self): return self.__sicaklik #set bölümü def set_sicaklik(self, deger): if deger < -273: raise ValueError("Temperature below -273 is not possible") self.__sicaklik = deger |
get fonksiyonu _sicaklik değerini döndürürken, set ile _sicaklik değeri ayarlanır. set bölümünde sıcaklık değerinin kontrolü de yapılıyor. Sıcaklık değeri -273’ten küçük girilirse raise ile bir exception üretilecektir. _sicaklik değişkeninin önündeki “_” bu değişkeni private yapar, bu sayede değiştirilmesi imkansız olur. Bu konu nesne yönelimli programlamada sarmallama (encapsulation) olarak adlandırılır. Ayrıca, yapıcı içinde set fonksiyonunu kullandık.
1 |
c = santigrat(-277) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-32-deee80ff618a> in <module>() ----> 1 c = santigrat(-277) <ipython-input-31-2e7be256a6a1> in __init__(self, sicaklik) 1 class santigrat: 2 def __init__(self, sicaklik = 0): ----> 3 self.set_sicaklik(sicaklik) 4 5 def fahrenhayt(self): <ipython-input-31-2e7be256a6a1> in set_sicaklik(self, deger) 12 def set_sicaklik(self, deger): 13 if deger < -273: ---> 14 raise ValueError("Sıcaklık 273 altı olamaz") 15 self._sicaklik = deger ValueError: Sıcaklık 273 altı olamaz |
Evet, artık sicaklik değeri kontrol edilebilir duruma geldi. Şimdi get ve set fonksiyonları üzerinden sicaklik değerini değiştirelim.
1 2 3 4 |
c = santigrat(-77) print(c.get_sicaklik()) c.set_sicaklik(15) print(c.get_sicaklik()) |
1 2 |
-77 15 |
Buraya kadar bir problem yok. Get ve set üzerinden çalıştığınız sürece değişken private olarak kalır. Ancak, Python değişkenleri dışarıdan tanımlamaya olanak tanır. Örneğin,
1 2 |
c._sicaklik = -400 print(c.get_sicaklik()) |
Program hata vermeden -400 değerini döndürür. Çünkü fonksiyon üzerinde veri alınmamıştır. Bu tanımlamadan sonra değişkenin private özelliği de bozulur. Şimdi benzer çözümü property ile yapalım.
15.2. Property çözümü
Property konusunu C# kullanmış geliştiriciler hatırlar. Bir fonksiyon yerine direkt bir değişken üzerinden get ve set işlemlerine olanak sağlayan bir yapı idi. Değişken olmasına rağmen bir kontrol işlemine tabii tutulabiliyordu. Property konusu Python’da çok benzer bir yapıya sahiptir. Basit bir örnekle açıklayalım:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class santigrat: def __init__(self, sicaklik = 0): self.sicaklik = sicaklik def fahrenhayt(self): return (self.sicaklik * 1.8) + 32 #get bölümü def get_sicaklik(self): print("Değer döndürülüyor") return self.__sicaklik #set bölümü def set_sicaklik(self, deger): print("Değer alınıyor") if deger < -273: raise ValueError("Sıcaklık 273 altı olamaz") self.__sicaklik = deger #propety özelliği ekleniyor sicaklik = property(get_sicaklik,set_sicaklik) |
sicaklik adında bir değişkenle get_sicaklik ve set_sicaklik fonksiyonları eşleştiriyor. Bundan sonra sicaklik üzerinden yapılan işlemler bu fonksiyonlar üzerinden geçer. Örneğin,
1 2 3 4 5 6 7 |
c = santigrat(-77) print(c.sicaklik) c.sicaklik = 50 print(c.sicaklik) #hala fonksiyonlarda çalışır, property karşılıkları hemen yanında c.set_sicaklik(65) # c.sicaklik = 65 print(c.get_sicaklik()) # print(c.sicaklik) |
1 2 3 4 5 6 7 8 9 |
Değer alınıyor Değer döndürülüyor -77 Değer alınıyor Değer döndürülüyor 50 Değer alınıyor Değer döndürülüyor 65 |
Her ne kadar property tanımlanmış olsa da fonksiyonlarda hala kullanılabilir. Ama örnekte olduğu gibi property kodu daha kısa ve kullanımı daha kolaydır. Peki property üzerinden -273’ten küçük bir değer alalım.
1 |
c.sicaklik = -500 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-17-680598b776e4> in <module>() 1 c.set_sicaklik(65) 2 print(c.get_sicaklik()) ----> 3 c.sicaklik = -500 <ipython-input-5-1285d8805e28> in set_sicaklik(self, deger) 15 print("Değer alınıyor") 16 if deger < -273: ---> 17 raise ValueError("Sıcaklık 273 altı olamaz") 18 self._sicaklik = deger 19 ValueError: Sıcaklık 273 altı olamaz |
Fonksiyona girdiği görülüyor. Ancak, 15.1’de olduğu gibi _sicaklik değeri sınıf dışından, nesne üzerinden değiştirilebilir ve erişilebilir. Bu dudumda yine set bölümündeki kontrol aşılmış olur.
1 2 |
c._sicaklik = -390 print(c.sicaklik) |
1 2 |
Değer döndürülüyor -390 |
Kod hata vermeden çalıştı. Bu tür hatalar oluşmaması için fonksiyon veya property üzerinden değişkenlere ulaşmanızı öneririm. Her ne kadar Python “_” kullanımda private özelliği gibi görse de bir kez aşıldığında kullanılabiliyor.
15.3. Property fonksiyonu
Python’da property() bir property nesnesi oluşturmak ve döndürmek için var olan yerleşik bir fonksiyondur. Aldığı parametreler:
property(fget=None, fset=None, fdel=None, doc=None)
fget get fonksiyonu işaret etmek, fset set fonksiyonunu işaret etmek, fdel silme fonksiyonunu işaret etmek ve doc ise yorum/açıklama için kullanılır. Tüm parametreler seçimliktir.
15.4. Property ve decorators
Python’da geliştiriciler decorator kullanarak property tanımlama şansına sahiptir. Örneğimizi decorator kullanarak tekrar düzenleyelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class santigrat: def __init__(self, sicaklik = 0): self.sicaklik = sicaklik def fahrenhayt(self): return (self.sicaklik * 1.8) + 32 @property def sicaklik(self): print("Değer döndürülüyor") return self.__sicaklik @sicaklik.setter def set_sicaklik(self, deger): print("Değer alınıyor") if deger < -273: raise ValueError("Sıcaklık 273 altı olamaz") self.__sicaklik = deger |
Bir önceki sınıfa göre get bölümü direkt property ismini aldı. @property decorator’ı eklendi. Ardından @sicaklik.setter ile set fonksiyonuna bağlandı. Seçim sizin, hepsi birbirinden güzel çözümler…