“Hiç uğraşamam…”

twitter.com/brsyuksel

Sınıf Özellikleri ve Yeni Stil Sınıflar

Başlık biraz tuhaf oldu, insan ilk bakışta SOAP’ın ÇORBA olarak çevirildiği programlama kitaplarından ( Teşekkürler M.Çileli :) ) alıntılar arıyor. Buna yakın çevirilere rastlarsanız pek de şaşırmayın. O kitaplara bakakaldığınız anları yaşayıp ardına verdiğiniz tepki içerisinde adımın geçmemesi için belirli terim ve konu başlıklarının yanında ingilizcelerini yazmayı ihmal etmedim :D Bazı konu başlıklarını ise direkt ingilizce verdim. Lafı daha fazla uzatmadan konuya geçiyorum.

Yeni stil sınıflara (new-style classes) geçmeden önce, eski yapıda da kullanabildiğimiz Özel Yöntem İsimlerinden (Special Method Names) bahsedeyim.

Temel Uyarlama (Basic Costumization)

__init__(self[, ...])
Bildiğiniz gibi sınıf örneklendirildiğinde (instance) çalışır. Örneği hazırlama (initialization) amaçlıdır.

__del__(self)
Sınıf örneği silinirken çalışır. Tahmin edebileceğiniz gibi silme için kullanılır.

__repr__(self)
Sınıf örneği repr() yerleşik fonksiyonuna parametre olarak verildiğinde çalışır.

__str__(self)
Sınıf örneği str() yerleşik fonksiyonuna parametre olarak verildiğinde ve “print” ifadesiyle gönderildiğinde çalışır.

__hash__(self)
Sınıf örneği hash() yerleşik fonksiyonuna parametre olarak verildiğinde çalışır.

__nonzero__(self)
Sınıf örneğinin True/False değer testi yapıldığında çalışır. 0 veya 1 sonucunu dönmelidir.

Şimdi diğer özel yöntemlere geçmeden önce yukarıda açıkladıklarım için basit bir örnek yapalım.

  1. class Sinif:
  2.         def __init__(self,ornStr=""):
  3.                 self.ornStr = ornStr
  4.                 self.ornList = ["a","b","c"]
  5.         def __del__(self):
  6.                 print "__del__ çalıştı"
  7.         def __repr__(self):
  8.                 return repr(self.ornList)
  9.         def __str__(self):
  10.                 return str(self.ornStr)
  11.         def __hash__(self):
  12.                 return hash(self.ornStr)
  13.         def __nonzero__(self):
  14.                 if len(self.ornStr) > 0:
  15.                         return 1
  16.                 else:
  17.                         return 0
  18.  
  19. ornek = Sinif("deneme") #__init__ calisti
  20. print repr(ornek) #__repr__ calisti, cikti "['a','b','c']"
  21. x = str(ornek) #__str__ calisti, x degiskenine "deneme" degeri atandi
  22. print ornek #__str__ calisti, cikti "deneme"
  23. print hash(ornek) #__hash__ calisti
  24. print bool(ornek) #__nonzero__ calisti, ornStr uzunlugu 0′dan buyuk oldugu icin True dondu
  25. if ornek is True: print "sonuc:true" #__nonzero__ calisti
  26. del ornek #__del__ calisti, ornek silindi, cikti "__del__ çalıştı"

__lt__(self,diger)
__le__(self,diger)
__eq__(self,diger)
__ne__(self,diger)
__gt__(self,diger)
__ge__(self,diger)
Aynı sınıfın iki farklı örneği arasında mantıksal karşılaştırmalar sırasında çalışırlar. Sırasıyla x<=y, x==y, x!=y (x<>y),x>y, x>=y
Sonuç olarak Boolean değer döndürmek daha verimli olacaktır.

__cmp__(self,diger)
Eğer yukarıdaki fonksiyonlar tanımlanmamışsa mantıksal karşılaştırmalar sırasında çalışır. selfdiger durumunda pozitif bir sayı dönmelidir.

Yukarıdaki fonksiyonlar için basit birer örnek hazırlayalım.

  1. class Sinif:
  2.         def __init__(self,sayi=0):
  3.                 self.sayi = sayi
  4.         def __lt__(self,diger):
  5.                 if self.sayi < diger.sayi:
  6.                         print "kucuktur!"
  7.                         return True
  8.                 else:
  9.                         print "kucuk degil!"
  10.                         return False
  11.  
  12. orneka = Sinif(1)
  13. ornekb = Sinif(2)
  14.  
  15. orneka < ornekb #__lt__ calisti, cikti "kucuktur!"
  16. if orneka < ornekb: print "sonuc:kucuktur!" #sart saglanir
  1. class Sinif:
  2.         def __init__(self,sayi=0):
  3.                 self.sayi = 0
  4.         def __cmp__(self,diger):
  5.                 if self.sayi < diger.sayi:
  6.                         return -1
  7.                 elif self.sayi == diger.sayi:
  8.                         return 0
  9.                 else:
  10.                         return 1
  11.  
  12. orneka = Sinif(1)
  13. ornekb = Sinif(2)
  14. orneka < ornekb #True orneka == ornekb #False orneka > ornekb #False
  15. #Burada __cmp__’nin -1,0 ya da 1 donmesine gore sonucun True/False olacagini yanilgisina kapilmayin. Verdiginiz degerler dogrultusunda yaptiginiz karsilastirmalar sonucu True/False sonucu alacaksiniz.

Niteliklere Erişimi Uyarlamak (Costumizing Attribute Access)

__getattr__(self,isim)
İstenilen nitelik bulunamadığı zaman çalışır.

__setattr__(self,isim,deger)
Örneğin niteliğine değer ataması yapılırken çalışır.

__delattr__(self,isim)
Örneğin niteliğinin “del” ifadesi ile silinmesi sırasında çalışır.

  1. class Sinif:
  2.         a = 1
  3.         b = 2
  4.         def __getattr__(self,isim):
  5.                 print "getattr!"
  6.                 self.__class__.__dict__[isim] = -1
  7.                 return -1
  8.         def __setattr__(self,isim,deger):
  9.                 print "setattr!"
  10.                 self.__class__.__dict__[isim] = deger
  11.         def __delattr__(self,isim):
  12.                 print "delattr!"
  13.                 del self.__class__.__dict__[isim]
  14.  
  15. ornek = Sinif()
  16. print ornek.a #herhangi bir metod calismadi, cikti 1
  17. print ornek.b #herhangi bir metod calismadi, cikti 2
  18. print ornek.c #__getattr__ calisti, sinif niteliklerinin barindirildigi sozlukte (__dict__) "c" anahtarina karsilik -1 degeri atandi, cikti "getattr! -1"
  19. print ornek.c #herhangi bir metod calismadi. bir onceki adimda "c" niteligi zaten olusturuldu, cikti -1
  20. ornek.a = 4 #__setattr__ calisti, nitelige istenen deger verildi, cikti "setattr!"
  21. del ornek.a #__delattr__ calisti, nitelik silindi, cikti "delattr!"
  22.  
  23. """
  24. Burada sinifin nitelik sozlugu (__dict__) yerine "self.isim = deger" ifadesini kullanmamamizin sebebi, "self.isim = deger" ifadesinin rekursif fonksiyon gibi davranmasina sebep olmasi. Bu nedenle bu ifade yerine sozlugu tercih etmek gerekiyor.
  25. """

Çağrılabilir Nesne Olarak İşlemek (Emulating Callable Objects)

__call__(self[, ...])
Sınıf örneğini bir fonksiyon gibi çağrıldığında çalışır.

  1. class Sinif:
  2.         def __call__(self,*arg):
  3.                 print arg[:2]
  4.  
  5. ornek = Sinif()
  6. ornek(1,2,3) #__call__ calisti, gonderilen ilk 2 arguman yazdirildi, cikti (1,2)

Kapsayıcı Tür Olarak İşlemek (Emulating Container Types)

__len__(self)
Sınıf örneği len() yerleşik fonksiyonuna parametre olarak verildiğinde çalışır.

__getitem__(self,anahtar)
Sınıf örneğinin bir “ögesi” (örn:x[0],x["anahtar"] vb) istendiği zaman çalışır.

__setitem__(self,anahtar,deger)
Sınıf örneğinin bir ögesine değer ataması yapılırken çalışır.

__delitem__(self,anahtar)
Sınıf örneğinin bir ögesi silinirken çalışır.

__iter__(self)
Sınıf örneği iter() yerleşik fonksiyonuna parametre olarak verildiğinde çalışır. Bir iterator nesnesi dönmelidir.

__contains__(self,item)
Bir ögenin sınıf örneği ögeleri arasında olup olmadığı (1 in x, 1 not in x) denetlenirken çalışır. Boolean değer dönmelidir.

  1. class Sinif:
  2.         dct = {"a1":"d1","a2":"d2","a3":"d3"}
  3.         def __getitem__(self,anahtar):
  4.                 if self.__class__.__dict__["dct"].has_key(anahtar):
  5.                         return self.__class__.__dict__["dct"][anahtar]
  6.         def __setitem__(self,anahtar,deger):
  7.                 self.__class__.__dict__["dct"][anahtar] = deger
  8.         def __delitem__(self,anahtar):
  9.                 del self.__class__.__dict__["dct"][anahtar]
  10.         def __iter__(self):
  11.                 return iter(self.__class__.__dict__["dct"])
  12.         def __contains__(self,anahtar):
  13.                 if anahtar in self.__class__.__dict__["dct"]:
  14.                         return True
  15.                 else:
  16.                         return False
  17.  
  18. ornek = Sinif()
  19. print ornek["a1"] #__getitem__ calisti, cikti "d1"
  20. print ornek["a4"] = "d4" #__setitem__ calisti, sozlukte "a4" anahtarina karsilik "d4" degeri verildi
  21. del ornek["a4"] #__delitem__ calisti, sozlukten "a4" anahtari silindi
  22. iter(ornek) #__iter__ calisti, dictionary-keyiterator nesnesi olusturuldu
  23. "a3" in ornek #__contains__ calisti, sozluk "a3" anahtarina sahip oldugu icin True degeri donduruldu
  24. "a4" in ornek #__contains__ calisti, sozluk "a4" anahtarina sahip olmadigi icin False degeri donduruldu
  25.  
  26. """
  27. Burada da __*attr__ orneginde oldugu gibi "self[anahtar] = deger" ifadesinin rekursif cagri yapmasindan dolayi onun yerine sinifin nitelik barindiran sozlugunu tercih ediyoruz.
  28. """

Sayısal Tür Olarak İşlemek (Emulating Numeric Types)

__add__(self,diger)
__sub__(self,diger)
__mul__(self,diger)
__floordiv__(self,diger)
__mod__(self,diger)
__divmod__(self,diger)
__pow__(self,diger[, mod])
__lshift__(self,diger)
__rshift__(self,diger)
__and__(self,diger)
__xor__(self,diger)
__or__(self,diger)
Aynı sınıfın iki farklı örneği arasında matematiksel işlemler yapılırken çalışırlar. Sırasıyla +,-,*,//,%,divmod(),pow(),<<,>>,&,^,|
Örneğin x ve y’nin sınıf örnekleri olduğu düşünürsek, x+y işlemi yapıldığında x.__add__(y) çalışır.

__div__(self,diger)
__truediv__(self,diger)
Aynı sınıfın iki farklı örneği arasında bölme işlemi yapılırken çalışırlar. Eğer __future__ kütüphanesinin division modülü uygulamaya dahil edilmişse __truediv__(), edilmemişse __div__() fonksiyonu çalışır.

  1. class Sinif:
  2.         def __init__(self,sayi):
  3.                 self.sayi = sayi
  4.         def __add__(self,diger):
  5.                 self.sayi = self.sayi + diger.sayi
  6.  
  7. orneka = Sinif(10)
  8. ornekb = Sinif(20)
  9. orneka + ornekb #__add__ calisti, orneka.sayi degeri artık 30
  10. print orneka.sayi #cikti 30
  11.  
  12. #Sanirim digerleri icin ornek yapmaya gerek yok :)

Bu arada, ben floordiv’den (//) bi’ haberdim. Siz de benim gibi floordiv’in ne olduğunu bilmiyorsanız şöyle ifade edeyim. f(x,y): return (x – (x%y)) / y
Bölen olarak verilen sayının bölünende kaç adet bulunduğunu veriyor diyelim. Bölme işleminin tam sayı olarak sonuç vermesiyle karıştırmayın :)

__radd__(self,diger)
__rsub__(self,diger)
__rmul__(self,diger)
__rdiv__(self,diger)
__rmod__(self,diger)
__rdivmod__(self,diger)
__rpow__(self,diger)
__rlshift__(self,diger)
__rrshift__(self,diger)
__rand__(self,diger)
__rxor__(self,diger)
__ror__(self,diger)
İki örnek arasında matematiksel işlem yaparken ifadenin sağında kalan örneğin tanımlanmış fonksiyonu çalışır.
Örneğin x ve y’nin sınıf örnekleri olduğunu düşürsek, x+y işlemi sırasında y.__radd__(x) çalışır.

__iadd__(self,diger)
__isub__(self,diger)
__imul__(self,diger)
__idiv__(self,diger)
__imod__(self,diger)
__ipow__(self,diger[, mod])
__ilshift__(self,diger)
__irshift__(self,diger)
__iand__(self,diger)
__ixor__(self,diger)
__ior__(self,diger)
İki örnek arasında “kendi üzerine” matematiksel işlem yapılırken çalışır. Sırasıyla +=, -=, *=, /=, %=, **=, <<=, >>=, &=, ^=, |=
Örneğin x ve y’nin sınıf örnekleri olduğunu düşünürsek, x += y işlemi sırasında x.__iadd__(y) çalışır.

__neg__(self)
__pos__(self)
__abs__(self)
__invert__(self)
Sınıf örneğinin matematiksel olarak işaretini değiştirme, mutlak değerini alma ( abs() fonksiyonuna parametre olarak gönderme ) ve bitsel değilini alma işlemleri sırasında çalışırlar. Örneğin, x’in sınıf örneği olduğunu düşünürsek; -x ifadesi x.__neg__(), +x ifadesi x.__pos__(), abs(x) fonksiyonu x.__abs__() ve ~x ifadesi x.__invert__() çağrılarını yapar.

__complex__(self)
__int__(self)
__long__(self)
__float__(self)
Sınıf örneği sırasıyla complex(), int(), long(), float() fonksiyonlarına parametre olarak gönderildiğinde çalışırlar. Bu fonksiyonlar sonuç olarak uygun türü dönmelidir.

__oct__()
__hex__()
Sınıf örneği sırasıyla oct(), hex() fonksiyonlarına parametre olarak gönderildiğinde çalışırlar. Bu fonksiyonlar string bir değer dönmelidir.

__coerce__()
Python Reference Manuel kitabında “karışık-mod” üzerinde aritmetik işlemler yaparken çalıştığı aktarılmış. Bir kaç kurala sahip. Basit bir şekilde kural şu: “verileri işlem operatörünün işleyebileceği şekle dönüştür ve işlemin kurallarına uyacak şekilde bir tüpün 2 ögesi olarak döndür”. Sonuç olarak uygun verileri bir tüpün 2 ögesi olarak ya da işlem başarısızlığı durumunda None dönmesi gerekiyor. Bunu bir örnekle anlatmak uygun olacak.

  1. class Sinif:
  2.         ornStr = "%s deneme %s"
  3.         def __coerce__(self,diger):
  4.                 return (self.ornStr,diger)
  5.  
  6. ornek = Sinif()
  7. print ornek % ("dnm","dnm")
  8. """
  9. __coerce__ calisti, bir sinif ornegi ve bir tup ile moduler aritmek yapmaya calistik ancak __coerce__ fonksiyonumuzu moduler aritmetik islemiyle "string formatter" olacak sekilde tasarladigimiz icin gorevini o sekilde yerine getirdi.
  10. cikti "dnm deneme dnm"
  11. """
  12. print ornek * 3 # Bu sefer "%s deneme %s%s deneme %s%s deneme %s" ciktisini aldik

Yeni Stil Sınıflar (New-Style Classes)

Python 2.2 ile gelen yeni stil sınıfların, “class Sinif:” diyerek itelediğimiz eski yapıya göre en önemli farkı yerleşik türlerden (built-in types: int,float,str,tuple,dict vs.) türetilebilir olmasıdır. Bunlardan türetilmediği durumda object türünden türetilmesi gerekir.

  1. class Sinif(object): pass
  2. class MyList(list): pass

dict türünden türetilmiş bir sınıf örneği

  1. class mydict(dict):
  2.         def __init__(self):
  3.                 dict.__init__(self)
  4.         def __getitem__(self,anahtar):
  5.                 try:
  6.                         return dict.__getitem__(self,anahtar)
  7.                 except:
  8.                         return -1
  9.         def __setitem__(self,anahtar,deger):
  10.                 dict.__setitem__(self,anahtar,deger+1) # deger+1 ;)
  11. """
  12. >>> x = mydict()
  13. >>> x
  14. {}
  15. >>> x["a"] = 0
  16. >>> x["a"]
  17. 1
  18. >>> x
  19. {‘a’: 1}
  20. >>> x["b"]
  21. -1
  22. """

__slots__

Sınıf içerisinde __slots__’u tanımladığınız zaman sınıf örneği __dict__’e sahip olmayacaktır. Bu nedenle sizin __slots__’u tanımlayarak belirlediğiniz nitelikler dışında herhangi bir nitelik oluşturulamaz. __slots__’u tanımlarken bir list ile değer ataması yapılmalıdır.

  1. class Sinif:
  2.         __slots__ = ["a","b"]
  3.         a = 1
  4.         b = 2
  5.  
  6. ornek = Sinif()
  7. print ornek.a #cikti 1
  8. print ornek.b #cikti 2
  9. ornek.a = 0 #a niteligi 0 degerine sahip oldu
  10. ornek.c = 3 #hata!

Descriptor

Bir sınıfın niteliğine erişim sırasında __get__(), __set__(), __delete__() metodları ile erişimin yönetilmesini sağlar. Bu yönteme “binding behavior” adı verilir. Descriptor yapısı; property,method,static method,class method ve super’in arkasındaki yapıdır.

  1. class Desc(object):
  2.         def __init__(self,deger):
  3.                 self.deger = deger
  4.         def __get__(self,nesne,sinif):
  5.                 print "GET: ", nesne,sinif
  6.                 return self.deger
  7.         def __set__(self,nesne,deger):
  8.                 print "SET: ", nesne
  9.                 self.deger = deger
  10.         def __delete__(self,nesne):
  11.                 print "DEL: ", nesne
  12.                 del self.deger
  13.  
  14. class Sinif(object):
  15.         x = Desc(10)
  16. """
  17. >>> ornek = Sinif()
  18. >>> ornek.x
  19. GET:  <__main__.Sinif object at 0xb7801fac>
  20. 10
  21. >>> ornek.x = 11
  22. SET:  <__main__.Sinif object at 0xb7801fac>
  23. >>> ornek.x
  24. GET:  <__main__.Sinif object at 0xb7801fac>
  25. 11
  26. >>> del ornek.x
  27. DEL:  <__main__.Sinif object at 0xb7801fac>
  28. """

Burada buna benzer bir yapıyı __*attr__ fonksiyonlarını kullanarak oluşturabileceğinizi düşünebilirsiniz ancak bildiğiniz gibi __*attr__ fonksiyonları sınıfın bütün niteliklerini kapsarken Descriptor’lar sadece tanımladığınız nitelikleri kapsayacaktır.

Property

Descriptor oluşturmanın diğer bir yolu denilebilir. Prototip’i şu şekilde:
property(fget=None, fset=None, fdel=None, doc=None)

  1. class Sinif(object):
  2.         def __init__(self):
  3.                 self.__d = 10
  4.         def __getx(self):
  5.                 print "__getx"
  6.                 return self.__d
  7.         def __setx(self,deger):
  8.                 print "__setx"
  9.                 self.__d = deger
  10.         def __delx(self):
  11.                 print "__delx"
  12.                 del self.__d
  13.  
  14.         OkuYazSil = property(fget=__getx,fset=__setx,fdel=__delx,doc="Oku-Yaz-Sil")
  15.         OkuYaz = property(fget=__getx,fset=__setx,doc="Oku-Yaz")
  16.         SadeceOku = property(fget=__getx,doc="Oku")
  17.  
  18. ornek = Sinif()
  19. ornek.OkuYazSil #cikti "__getx 10"
  20. ornek.OkuYazSil = 11 #cikti "__setx"
  21. ornek.OkuYazSil #cikti "__getx 11"
  22. ornek.OkuYaz #cikti "__getx 11"
  23. ornek.OkuYaz = 12 #cikti "__setx"
  24. """
  25. >>> del ornek.OkuYaz
  26. Traceback (most recent call last):
  27.  File "", line 1, in
  28. AttributeError: can’t delete attribute
  29. >>>
  30. """

Property Descriptor’u “Descriptor HowTo Guide” kitabında şu şekilde verilmiş. Descriptor yapısının daha iyi kavranması için incelemekte fayda var.

  1. class Property(object):
  2.         def __init__(self,fget=None,fset=None,fdel=None,doc=None):
  3.                 self.fget = fget
  4.                 self.fset = fset
  5.                 self.fdel = fdel
  6.                 self.__doc__ = doc
  7.  
  8.         def __get__(self,obj,objtype):
  9.                 if obj is None:
  10.                         return self
  11.                 if self.fget is None:
  12.                         raise AttributeError, "unreadable attribute"
  13.                 return self.fget(obj)
  14.  
  15.         def __set__(self,obj,value):
  16.                 if self.fset is None:
  17.                         raise AttributeError, "can’t set attribute"
  18.                 return self.fset(obj)
  19.  
  20.         def __delete__(self,obj):
  21.                 if self.fdel is None:
  22.                         raise AttributeError, "can’t delete attribute"
  23.                 return self.fdel(obj)

Static Method

Statik metodların özelliği, çağrılırken sınıf örneğini parametre olarak (self) almaya ihtiyaç duymaz. Bu nedenle sınıf örneklendirilmeden metod çağrılabilir. staticmethod() da property() gibi bir descriptor’dur.

  1. class Sinif:
  2.         def metod():
  3.                 print "statik metod"
  4.         stmetod = staticmethod(metod)
  5.  
  6. Sinif.stmetod() #cikti "static method"

staticmethod descriptor’u şu şekilde:

  1. class StaticMethod(object):
  2.         def __init__(self,f):
  3.                 self.f = f
  4.         def __get__(self,obj,objtype=None):
  5.                 return self.f

Class Method

Sınıf metodlarının statik metodlardan farkı, ilk parametre olarak sınıf referansını istemesidir. classmethod(), property() ve staticmethod() gibi bir başka descriptor’dur.

  1. class Sinif(object):
  2.         def metod(cls):
  3.                 print "sinif metodu", cls
  4.         clmetod = classmethod(metod)
  5.  
  6. Sinif.clmetod() #cikti "sinif metodu "
  7. ornek = Sinif()
  8. ornek.clmetod() #cikti "sinif metodu "

classmethod descriptor’u ise şu şekilde:

  1. class ClassMethod(object):
  2.         def __init__(self,f):
  3.                 self.f = f
  4.         def __get__(self,obj,objtype=None):
  5.                 print "obj-objtype", obj,objtype
  6.                 if objtype is None:
  7.                         objtype = type(obj)
  8.                 def newfunc(*args):
  9.                         return self.f(objtype,*args)
  10.                 return newfunc

Method Resolution Order, Super

Bir sınıfı tanımlarken bir veya birden çok sınıfı miras (inheritance) alarak tanımladığınızda sınıfa ait bir metodu çağırdığınız zaman aynı isime sahip metod taban sınıflarda (bases,superclass) yer alıyorsa çağrılmayacaktır. Örneğin:

  1. class ASinif(object):
  2.         def metod(self):
  3.                 print "A"
  4.  
  5. class BSinif(ASinif):
  6.         def metod(self):
  7.                 print "B"
  8.  
  9. ornek = BSinif()
  10. ornek.metod()# cikti "B"

Bu sorunu aşmak istediğiniz zaman kullandığınız genel yöntem şudur:

  1. class ASinif(object):
  2.         def metod(self):
  3.                 print "A"
  4.  
  5. class BSinif(ASinif):
  6.         def metod(self):
  7.                 ASinif.metod(self)
  8.                 print "B"
  9.  
  10. ornek = BSinif()
  11. ornek.metod()# cikti "A B"

Bu yöntem kimi zaman yeterli bir çözüm olabilir ancak işler biraz karışıp birden fazla sınıfı miras alarak çalıştığınızda şöyle bir sorunla karşılaşmanız olasıdır.

  1. class A(object):
  2.         def yaz(self):
  3.                 print "A"
  4.  
  5. class B(A):
  6.         def yaz(self):
  7.                 A.yaz(self)
  8.                 print "B"
  9.  
  10. class C(A):
  11.         def yaz(self):
  12.                 A.yaz(self)
  13.                 print "C"
  14.  
  15. class D(B,C):
  16.         def yaz(self):
  17.                 B.yaz(self)
  18.                 C.yaz(self)
  19.                 print "D"
  20.  
  21. ornek = D()
  22. ornek.yaz() #cikti "A B A C D"

Bu örnekte olduğu gibi eğer super sınıflara (base) ait metodların sadece bir defa ve bir düzen içerisinde çağrılmasını istiyorsanız super()’de çareyi bulacaksınız. super()’i anlatmadan önce __mro__’dan bahsedelim.

__mro__, yeni stil sınıflarda sınıfın super sınıflarını bir tüp (tuple) içerisinde niteliktir. super(), super sınıflara ait metodları çağırırken __mro__ niteliğinden sıradaki sınıfı bulmak için yararlanır.

Örneğin yukarıda tanımladığımız D sınıfının __mro__’ı şu şekildedir:

  1. """
  2. >>> D.__mro__
  3. (<class ‘__main__.D’>, <class ‘__main__.B’>, <class ‘__main__.C’>, <class ‘__main__.A’>, <type ‘object’>)
  4. """

super() kullanılarak yapılan çağrılar, bu listeyi takip ederek yapılır. Şimdi üstteki örneği super() kullanarak tekrar yazalım.

super() kullanılırken, parametreler şu şekilde olmalı:
super(type,obj) obj parametresi yani örnek, type ise örneği alınan sınıftır.

  1. class A(object):
  2.         def yaz(self):
  3.                 print "A"
  4.  
  5. class B(A):
  6.         def yaz(self):
  7.                 super(B,self).yaz()
  8.                 print "B"
  9.  
  10. class C(A):
  11.         def yaz(self):
  12.                 super(C,self).yaz()
  13.                 print "C"
  14.  
  15. class D(B,C):
  16.         def yaz(self):
  17.                 super(D,self).yaz()
  18.                 print "D"
  19.  
  20. ornek = D()
  21. ornek.yaz() #cikti "A C B D"
  22. #super cagrisina oncelik tanindigi icin boyle bir sonuc aliyoruz.

__new__

Bir süper sınıfın alt sınıfı (yeni stil sınıflarda her sınıf object sınıfının bir alt sınıfıdır) örneklendirildiğinde sanıldığının aksine __init__ fonksiyonundan önce __new__ fonksiyonu çağrılır. __new__ fonksiyonu bir staticmethod olup, ilk parametresi sınıftır. __new__ fonksiyonu sınıf örneğini döndükten sonra __init__ fonksiyonu çalışır.

  1. class inch(float):
  2.         def __new__(cls,arg=0.0):
  3.                 return float.__new__(cls, arg*0.0254)
  4.  
  5. inch(12) #cikti 3.048

Descintro’da yer alan Singleton örneğini incelemek __new__ fonksiyonunu kavramak açısından yararlı olacak.

  1. class Singleton(object):
  2.         def __new__(cls,*args,**kargs):
  3.                 it = cls.__dict__.get("__it__")
  4.                 if it is not None:
  5.                         return it
  6.                 cls.__it__ = it = object.__new__(cls)
  7.                 it.init(*args,**kargs)
  8.                 return it
  9.         def init(self,*args,**kargs):
  10.                 pass
  11.  
  12. class MySingleton(Singleton):
  13.         def init(self):
  14.                 print "init"
  15.         def __init__(self):
  16.                 print "__init__"
  17.  
  18. orneka = MySingleton() #cikti "init __init__"
  19. ornekb = MySingleton() #cikti "__init__"

Bu örneği açıklamak gerekirse: orneka oluşturulduğunda MySingleton sınıfının süper sınıfı Singleton’un __new__ metodu çağrılıyor. __new__ metoduna giden ilk parametre “MySingleton” sınıfı oluyor. MySingleton sınıfının nitelik sözlüğü __dict__’e bakılarak __it__ sınıf örneği ile tanımlanmışsa it değişkenine atanıp döndürülüyor, bu durumda MySingleton sınıfının sadece __init__ metodunun çağrılmasına sebep oluyor. Ancak burada MySingleton sınıfı henüz yeni örneklendirilmiş olduğu için __it__ tanımlanmamış, bu durumda “cls.__it__ = it = object.__new__(cls)” satırı çalışmaya başlıyor ve MySingleton sınıfı kendisinin __it__ niteliği ve Singleton sınıfının it değişkeni ile örneklendiriliyor. “it.init(*args, **kargs)” satırında gördüğünüz gibi MySingleton sınıfı örneğinin init metodu çağrılıyor. Ardına örnek döndürülüyor, __new__ metodunun görevi bitmiş olduğundan dolayı örneğin __init__ metodu çağrılmış oluyor. Bu nedenle “init __init__” çıktısını almış oluyoruz.

ornekb oluşturulduğunda tekrar Singleton sınıfının __new__ metodu çağrılıyor. Bu sefer MySingleton sınıfında __it__ niteliği sınıf örneği ile tanımlı olduğundan dolayı __new__ metodu bu tanımlı örneği dönüyor, bu sebepten dolayı sadece __init__ metodu çağrılmış oluyor ve “__init__” çıktısını alıyoruz.

Bu koca dökümanı geride bırakırken…
Bahsetmek isteyip de yazamadığım __metaclass__ konusu bir sonraki yazıda gelecek. Sonuçta burda yazdıklarımı ben de yazarken öğrendim ve __metaclass__ konusu son derece önemli bir konu. Şu sıralar küçük bir sağlık problemi yaşadığımdan dolayı fazla açıklamalarda bulunamadım, çoğunu özet geçmek zorunda kaldım. Bu nedenle aşağıda vereceğim kaynakları okumanızda yarar var.

descintro
Descriptor HowTo Guide
Python Language Referance
Introduction to New-Style Classes In Python – Micheal Fötsch
Python Attributes and Methods – Shalabh Chaturvedi
Python Types and Objects – Shalabh Chaturvedi

Ayrıca;
Bu blog’da yer alan Genel Kamu Lisansına (GPL, hepsi v3) sahip uygulamalar haricinde herhangi bir içerik herhangi bir lisans kapsamında değildir. İstenildiği gibi alınıp, kenarından köşesinden çırpılıp kullanılabilir. Kaynak gösterilmeden kullanılması bir nebze de olsa umrumda değildir. Burada tüm yazdıklarımı öğrenirken öğretmek amaçlı yazdığım için hatalar olabilir, kaynak göstermeden kullandığınız durumda ihale size kalır.

Tags: , , , , , , , , , ,
Posted in Python · Haziran 27th, 2010 · Comments (0)

No comments yet

Leave a Reply

Bağlantılar

Etiketler

çember özel yöntem isimleri apache2screen apache2screen.deb bele kuvvet bi_rle4 brute-force classic classes classmethod descriptors getanurse ic.lnx icontacts ileri c programlama image imagedraw kaba kuvvet kelime tahmin libnotify math math.cos math.sin matruşka matryoshka memory leak mesafe metaclass mysqli new-style classes oauth php5 property pynotify Python singleton special method names staticmethod steganografi steganography super twittell TwittellOAuth wchar yeni stil sınıflar __mro__

Meta