“Hiç uğraşamam…”
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
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.
__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.
__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.
__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.
__call__(self[, ...])
Sınıf örneğini bir fonksiyon gibi çağrıldığında çalışır.
__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.
__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.
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.
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.
dict türünden türetilmiş bir sınıf örneği
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.
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.
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.
Descriptor oluşturmanın diğer bir yolu denilebilir. Prototip’i şu şekilde:
property(fget=None, fset=None, fdel=None, doc=None)
Property Descriptor’u “Descriptor HowTo Guide” kitabında şu şekilde verilmiş. Descriptor yapısının daha iyi kavranması için incelemekte fayda var.
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.
staticmethod descriptor’u şu şekilde:
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.
classmethod descriptor’u ise şu şekilde:
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:
Bu sorunu aşmak istediğiniz zaman kullandığınız genel yöntem şudur:
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.
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:
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.
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.
Descintro’da yer alan Singleton örneğini incelemek __new__ fonksiyonunu kavramak açısından yararlı olacak.
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: classic classes, classmethod, descriptors, new-style classes, özel yöntem isimleri, property, special method names, staticmethod, super, yeni stil sınıflar, __mro__
Posted in Python · Haziran 27th, 2010 · Comments (0)
No comments yet