9. Sınıflar

Python'un sınıf mekanizması sınıfları çok az bir sözdizimi ve kavramsallıkla dile ekler. Sınıf mekanizması C++ ile Modula-3 karışımıdır denebilir. Python'da sınıflar modüllerdeki gibi kullanıcı ile tanımlar arasına somut engeller koymaz. Python'da sınıflara gücünü veren başlıca özelliklerini şöyle sıralayabiliriz: sınıflar çok sayıda sınıfı miras alabilir. Bu türetilmiş sınıflar, atalarının yöntemlerini değiştirerek kullanabilirler; ataları ile aynı isme sahip yöntemlerle, atasındaki yöntemleri çağırabilir. Nesneler özel verilere sahip olabilir.

C++ terminolojisinde, tüm sınıf üyeleri (veri üyeleri dahil) public ve tüm üye işlevler virtual'dir. Özel bir kurucu ya da yıkıcı yoktur. Modula-3'deki gibi, bir nesnenin yöntemlerinden üyelerine başvuru için bir kestirme yol yoktur: bir üye yöntem, dolaylı olarak çağrı ile sağlanan ve doğrudan nesneyi ifade eden bir ilk bağımsız değişken ile bildirilir. Smalltalk'daki gibi, sınıfların kendileri nesnelerdir (sınıfların ve nesnelerin kavramsal özelliklerine uymasa da böyledir). Python'da tüm veri türleri birer nesnedir. Bu onların yeniden isimlendirilebilmesi ve başka veri türlerine dahil edilmesi için bir yol sağlar. Ancak kullanıcı bunları genişletmek için temel sınıf olarak kullanamaz. Ayrıca Modula-3'de olmayan ama C++'da olan, özel yazımlı bir çok yerleşik işleç (aritmetik işleçler, alt indisleme, vs), sınıf gerçeklemeleri için yeniden tanımlanabilir.

9.1. Terminoloji hakkında...

Sınıflar hakkında konuşurken, evrensel olarak kabul edilmiş bir terminolojinin olmayışından dolayı arasıra Smalltalk ve C++ terimlerini kullanacağım. (Aslında, nesne yönelimi açısından Python'a daha çok benzeyen Modula-3 dili terimlerini kullanırdım; fakat bu dilden haberdar olan okuyucuların azınlıkta olduğunu sanıyorum.)

Ayrıca nesne yönelim kavramını bilenleri bir terminoloji tuzağına karşı uyarmalıyım: "Nesne" sözcüğü, Python'da "bir sınıfın gerçeklenmesi" anlamına gelmez. Smalltalk'da olmayan ancak C++ ve Modula-3'dekine benzer şekilde Python'daki veri türlerinin hepsi birer sınıf değildir: tamsayılar ve listeler gibi yerleşik veri türleri ile dosyalar sınıf değildir. Yine de tüm Python veri türleri için ortak bir kavramı paylaşmaları adına nesnelerdir demek yanlış olmaz.

Nesneler tek tek ve çoklu isimlerle (çoklu etki alanları içinde), aynı nesneye bağlı olabilir. Bu diğer dillerde kod isimlendirme (aliasing) olarak bilinir. Bu genellikle Python'da ilk bakışta anlaşılmaz ve değişmez temel türler (sayılar, dizgeler, demetler) ile çalışırken yoksayılabilir. Yine de kod isimlendirme, listeler sözlükler gibi değiştirilebilen nesneler ve yazılım dışındaki öğeler (dosyalar, pencereler, vs) için kullanılan bazı türlerinde katılımıyla Python kodunun kavramsallaştırılmasında (kasıtlı!) bir etkiye sahiptir. Bazı yönleriyle kod isimlendirme göstergelere benzer bir davranış sergilediğinden genellikle, yazılım yararına kullanılmıştır. Örneğin, sadece göstergesi aktarıldığından bir nesnenin aktarılması kolaydır; eğer bir işlev, bir bağımsız değişken olarak aktarılan bir nesneyi değiştirirse, işlevi çağıran değişikliği görecektir - bu, Pascal'daki gibi iki farklı bağımsız değişken aktarma gereksinimini ortadan kaldırır.

9.2. Python Etki ve İsim Alanları

Sizi sınıflar ile tanışmadan önce, biraz da Python'un etki alanı kurallarından bahsetmem gerek. Sınıf tanımlamalarını tam olarak anlamak için etki ve isim alanlarının nasıl çalıştığını bilmeniz gerek. Bu konudaki bilgiler ileri seviyedeki her Python yazılımcısı için yaralıdır.

Birkaç tanım ile işe koyulalım.

Bir isim alanı isimler ile nesnelerin eşleşmesidir. Çoğu isim alanı şu anda Python sözlükleri olarak kodlanmışlardır, fakat bu hiçbir şekilde fark edilmez ve gelecekte değiştirilebilir. İsim alanlarına örnekler: yerleşik isimler kümesi (abs() gibi işlevler ve yerleşik istisna isimleri vs.), bir modül içindeki global isimler ve bir işlev çağrısındaki yerel isimler. Bir nesnenin özellikler kümesi de bir isim alanıdır. İsim alanlarına ilişkin bilinecek önemli şey farklı isim alanlarındaki isimlerin birbirileri ile hiçbir ilişkisi olmadığıdır. Örneğin, iki farklı modül karışıklık yaratmadan “maksimize” adlı birer işlev tanımlayabilir; kullanıcılar bu işlevleri önlerine modül adını ekleyerek kullanır.

Bu arada, bir noktadan sonra yazılan her herhangi bir isim için öznitelik sözcüğünü kullanıyorum. Örneğin, z.real ifadesinde real, z nesnesinin bir özniteliğidir. Modül içindeki isimlere atıflar da öznitelik atıflarıdır: modulAdi.fonkAdi ifadesinde modulAdi bir modül nesnesidir ve fonkAdi bunun bir özniteliğidir. Bir modülün öznitelikleri ile içinde tanımlı global değişkenler aynı isim alanını paylaşır. [71]

öznitelikler salt okunur veya yazılabilir olabilir. Yazılabilir oldukları durumda özniteliklere atama yapmak mümkündür. Modül öznitelikleri yazılabilirdir: modulAdi.sonuc = 42 gibi bir ifade kullanabilirsiniz. Yazılabilir öznitelikleri del deyimini kullanarak silmek de mümkündür. Örneğin del modulAdi.sonuc ifadesi modulAdi nesnesinden sonuc isimli özniteliği siler.

Farklı anlarda yaratılan isim alanlarının farklı ömürleri olur. Yerleşik isimleri içeren isim alanı Python yorumlayıcısı çalıştırıldığında yaratılır ve asla silinmez. Bir modüle ait global isim alanı modül tanımı okunduğunda yaratılır ve genellikle yorumlayıcı çalıştığı sürece silinmez. Yorumlayıcının bir dosyadan veya etkileşimli olarak çalıştırdığı deyimler de __main__ isimli bir modüle ait kabul edilir ve bunların da kendi global isim alanı vardır. Yerleşik isimler de __builtin__ isimli bir modülde bulunur.

Bir işleve ait yerel isim alanı işlev çağırıldığında yaratılır ve işlevden dönüldüğünde veya işlev içinde ele alınmamış bir istisna gerçekleştiğinde silinir. Tabii ki özyinelemeli çağrıların herbiri kendi yerel isim alanına sahiptir.

Bir etki alanı bir isim alanının doğrudan erişilebildiği bir metin bölgesidir. Burada “doğrudan erişilebilir” ifadesinin anlamı, yetersiz bir isim atfının isim alanında isim bulmaya teşebbüs etmesidir.

Etki alanları statik olarak belirlenmelerine rağmen, dinamik olarak kullanılır. İcranın herhangi bir anında isim alanlarına doğrudan erişilebilen iç içe geçmiş en az üç etki alanı vardır: ilk aranan ve yerel isimleri içeren en iç etki alanı; en yakın olanından başlanarak aranan çevreleyen işlevlerin isim alanları; daha sonra aranan ve o andaki modülün global değişkenlerini içeren orta etki alanı; ve yerleşik isimlerin bulunduğu isim alanı olan en dış etki alanı (en son aranır).

Eğer bir isim global olarak tanımlanmış ise tüm atıflar ve atamalar doğrudan modülün global isimlerini barındıran orta etki alanına gider. Zaten, en iç etki alanının dışındaki tüm isimler salt okunurdur.

Genellikle yerel etki alanı o an içinde bulunulan (yazılım metninde) işlevin yerel isimlerine atıfta bulunur. İşlevlerin dışında yerel etki alanı global etki alanı ile aynı isim alanıdır: modülün isim alanı. Sınıf tanımlamaları ayrıca yerel etki alanı içerisine bir başka isim alanı daha ekler.

Etki alanlarının metne bağlı olarak belirlendiğini anlamak önemlidir. Bir modül içinde tanımlı bir işlevin global etki alanı o modülün isim alanıdır; nereden çağırıldığı ya da hangi farklı isim ile çağırıldığı bir fark yaratmaz. Diğer yandan, asıl isim araması icra anında dinamik olarak yapılır; ancak dilin tanımı “derleme” sırasında yapılan statik isim çözümlemeye doğru değişmektedir ve dinamik isim çözümlemeye güvenmemelisiniz! Örneğin, yerel değişkenler şu anda statik olarak belirlenmektedir.

Python'a özgü bir tuhaflık da atamaların her zaman en iç etki alanına gitmesidir. Atamalar veri kopyalamaz; sadece nesnelere isimler bağlar. Aynı şey silme işlemleri için de geçerlidir: del x ifadesi x'in yerel etki alanı tarafından atfedilen isim alanındaki bağını kaldırır. Aslında, yeni isimler yaratan tüm işlemler yerel etki alanını kullanır. İşlev tanımları ve import deyimleri modül veya işlev adını yerel etki alanına bağlar. Bir değişkenin global etki alanında bulunduğunu belirtmek için global deyimi kullanılabilir.

9.3. Sınıflara İlk Bakış

Sınıflar ile bir miktar yeni sözdizim ve kavram ile üç yeni nesne türü tanıtacağız.

9.3.1. Sınıf Tanımlama

Sınıf tanımlamanın en basit şekli şöyledir:

class SinifAdi:
    <deyim-1>
    .
    .
    .
    <deyim-N>

Sınıf tanımlamaları, işlev tanımlamalarında (def deyimleri) olduğu gibi etkin olmaları için önce işletilmeleri gerekir. (Yanlışlıkla sınıf tanımlarını if deyimleri veya işlev içlerine koymamaya dikkat edin.)

Pratikte bir sınıf tanımının içindeki deyimler genellikle işlev tanımları olur; fakat başka deyimler de kullanmak mümkün ve yararlıdır (buna daha sonra yine değineceğiz). Sınıf içindeki işlev tanımlarının bağımsız değişken listesi kendilerine özgü bir şekle sahiptir; ancak buna da daha sonra değineceğiz.

Bir sınıf tanımına girildiğinde yeni bir isim alanı (name space) oluşturulur ve bu yerel etki alanı (scope) olarak kullanılır. Yerel değişkenlere yapılan bütün atamalar bu yeni isim alanına gider. Yeni tanımlanan işlevlerin isimleri de buraya eklenir.

Bir sınıf tanımı normal olarak tamamlandığında bir sınıf nesnesi yaratılmış olur. Bu, temel olarak, sınıf tanımının oluşturduğu isim alanı etrafında bir örtüdür. Sınıf nesnelerini bir sonraki bölümde daha yakından tanıyacağız. Orjinal etki alanı (sınıf tanımına girilmeden önce etkin olan) yine eski yerini alır ve sınıf nesnesi de buna sınıf tanımında kullanılan isim (örnekteki SinifAdi) ile dahil olur.

9.3.2. Sınıf Nesneleri

Sınıf nesneleri iki tür işlemi destekler: özniteliklere başvuru ve sınıfın örneklenmesi.

Özniteliklere başvuru için Python'da bütün özniteliklere erişmek için kullanılan standart sözdizim kullanılır: nesne.isim. Kullanılabilecek öznitelik isimleri sınıfın nesnesi oluşturulurken sınıfın isim alanında bulunan bütün isimlerdir. Sınıf tanımımız aşağıdaki gibi ise:

class benimSinif:
    "Basit bir sınıf örneği."
    i = 12345
    def f(self):
        return 'Merhaba'

benimSinif.i ve benimSinif.f bir tamsayı ve bir yöntem nesnesi geri döndüren geçerli öznitelik başvurularıdır. Sınıf özniteliklerine atama yapmak da mümkündür. Örneğin atama yoluyla benimSinif.i değeri değiştirilebilir. Ayrıca, __doc__ da geçerli bir öznitelik olup sınıfa ait belgeleme dizgesini geri döndürür: "Basit bir sınıf örneği.".

Sınıfın gerçeklenmesi, işlev sözdizimini kullanır. Sınıf nesnesini yeni bir sınıf gerçeklemesi geri döndüren bağımsız değişkeniz bir işlevmiş gibi düşünebilirsiniz. Örneğin yukarıda tanımladığımız sınıf için:

x = benimSinif()

Yeni bir sınıf gerçeklemesidir ve sınıf x yerel değişkenine atanarak bir nesne oluşur.

Gerçeklenme işlemi (bir sınıf nesnesini ``çağırmak'') boş bir nesne yaratır. Pek çok sınıf nesnesinin bilinen bir ilk durumda oluşturulması istenir. Bu yüzden bir sınıfta __init__() adlı özel yöntem şu şekilde tanımlanabilir:

def __init__(self):
    self.data = []

Bir sınıfın __init__() yöntemi tanımlanmış ise sınıf gerçeklenmesi işlemi, yeni sınıf gerçeklemesi sırasında bu yöntemi otomatik olarak çağırır.

Daha fazla esneklik için __init__() yönteminin bağımsız değişkenleri da olabilir. Bu durumda sınıfın gerçeklenmesinde kullanılan bağımsız değişkenler __init__() yöntemine aktarılır.[72] Örnek:

>>> class karmasikSayi:
...     def __init__(self, gercekKsm, sanalKsm):
...         self.g = gercekKsm
...         self.s = sanalKsm
...
>>> x = karmasikSayi(3.0, -4.5)
>>> x.g, x.s
(3.0, -4.5)

9.3.3. Örnek Nesneler

Nesnelerle ne yapabiliriz? Bunlar ile yapabileceğimiz tek şey öznitelikleri ile uğraşmaktır. Nesnelerin iki tür özniteliği vardır. Bunların ilki veri öznitelikleridir. Veri özniteliklerinin tanımlanmış olması gerekmez; yerel değişkenlerde olduğu gibi bunlar da kendilerine ilk atama yapıldığında var olur. Örneğin x'in yukarıda tanımlanan benimSinif sınıfının bir örneği olduğunu düşünürsek aşağıdaki yazılım parçası 16 değerini geride bir iz bırakmadan yazdırır:

x.sayac = 1
while x.sayac < 10:
    x.sayac = x.sayac * 2
print x.sayac
del x.sayac

Nesnelerin ikinci tür öznitelikleri de yöntemlerdir. Yöntem bir sınıfa “ait olan” bir işlevdir. Python dilinde yöntemler sınıf örneklerine özgü değildir; diğer nesne türlerinin de yöntemleri olabilir. Örneğin liste nesnelerinin append, insert, remove, sort gibi yöntemleri vardır. Aşağıda yöntem terimini, aksi belirtilmediği sürece, sadece sınıflardan örneklenen nesnelerin yöntemleri anlamında kullanacağız.

Bir nesneye ilişkin geçerli öznitelik isimleri bunun sınıfına bağlıdır. Tanıma göre işlev olan tüm sınıf öznitelikleri o nesnenin yöntemleri olur. Bu yüzden örnek sınıfımız için x.f geçerli bir yöntem başvurusudur, çünkü benimSinif.f bir işlevdir, fakat x.i bir yöntem değildir, çünkü benimSinif.i bir işlev değildir. Burada şuna dikkat edelim: x.f ile benimSinif.f aynı şey değildir.

9.3.4. Yöntem Nesneleri

Genellikle bir yöntem şu şekilde doğrudan çağırılır:

x.f()

Bizim örneğimizde bu 'Merhaba' dizgesini geri döndürür. Bir yöntemi doğrudan çağırmak şart değildir: x.f bir yöntemdir ve bir değişkene saklanıp daha sonra çağırılabilir. Örneğin:

xf = x.f
while 1:
    print xf()

Sonsuza kadar "'Merhaba'" yazdırır.

Bir yöntem çağrıldığında tam olarak ne olur? x.f() çağrılırken bir bağımsız değişken kullanılmadığı halde f işlev tanımında bir bağımsız değişken kullanıldığını (self)fark etmişsinizdir. Bağımsız değişkene ne oldu acaba? Şüphesiz Python, bağımsız değişken gerektiren bir işlev bağımsız değişkeniz çağırıldığında bir istisna oluşturur. Cevabı belki de tahmin ettiniz: yöntemler, işlevin ilk bağımsız değişkeni olarak nesneyi alır. Başka bir deyişle Python'da yöntemler, kendi içinde, tanımlı olduğu nesneyi barındıran nesnelerdir. Örneğimizdeki x.f() çağrısı aslında benimSinif.f(x) ile aynıdır. Genel olarak, bir yöntemi n elemanlı bir bağımsız değişken listesi ile çağırmak, aynı işlevi başına nesnenin de eklendiği bir bağımsız değişken listesi kullanarak çağırmak ile aynı şeydir.

9.4. Bazı Açıklamalar

Veri öznitelikleri aynı isimli yöntem özniteliklerini bastırır. Büyük yazılımlardaki zor fark edilen isim çakışması hatalarından kaçınmak için çakışmaları en aza indirecek bir isimlendirme yöntemi kullanmak akıllıca olur. Yöntem isimlerini büyük harf ile başlatılırken, veri isimleri özel bir karakter (alt çizgi gibi) ile başlatılabilir. Yöntemler için fiil ve veri yapıları için isim olan kelimeler kullanılabilir.

Veri özniteliklerine o nesnenin kullanıcıları (“istemcileri”) başvuru yapabileceği gibi, yöntemler de bunlara başvuruda bulunabilir. Başka bir deyişle, sınıflar tamamen soyut veri türleri oluşturmak için kullanılamaz. Aslında, Python'da hiçbir şey veri saklamayı zorlamayı mümkün kılmaz.

Kullanıcılar nesnelerin veri özniteliklerini dikkatli kullanmalılar; çünkü istemciler yöntemler tarafından kullanılan önemli değişkenlere atama yaparak istenmeyen hatalara sebep olabilir. İstemcilerin, isim çakışmalarından kaçındıkları sürece, bir nesneye yöntemlerinin geçerliliğini etkilemeden kendi veri özniteliklerini ekleyebileceklerine dikkat edin.

Yöntem içinden veri özniteliklerine (ya da diğer yöntemlere!) başvuruda bulunmanın kestirme bir yolu yoktur. Bunun aslında yöntemlerin okunabilirliğini artırdığını düşünüyorum; bir yönteme göz attığınızda yerel değişkenler ile nesne değişkenlerini birbirilerine karıştırma şansı yoktur.

Usul olarak yöntemlerin ilk özniteliğine self adı verilir. Bu tamamen usule dayanır; self isminin Python için kesinlikle hiç bir özel anlamı yoktur. Bu usule uymazsanız yazılımınız diğer Python yazılımcıları tarafından daha zor okunur ve sınıf tarayıcısı (class browser) yazılımları da bu usule dayanıyor olabilir.

Sınıf özniteliği olan her işlev, o sınıfın nesneleri için bir yöntem tanımlar. İşlev tanımının sınıf tanımı içerisinde olması şart değildir; işlevi, sınıf içindeki yerel bir değişkene atamak da mümkündür. Örneğin:

# Sınıf dışında tanımlanmış işlev
def f1(self, x, y):
    return min(x, x+y)

class C:
    f = f1
    def g(self):
        return 'Merhaba'
    h = g

Şimdi f, g ve h'nın hepsi C sınıfının özellikleri oldular ve aynı anda C sınıfının nesnelerinin de yöntemleridirler (g ve h birbirinin tamamen aynısıdır). Bu tür uygulamanın genellikle sadece okuyucunun kafasını karıştırmaya yaradığına dikkat edin. Yöntemler self öğesinin yöntem özelliğini kullanarak diğer yöntemleri çağırabilir:

class Bag:
    def __init__(self):
        self.data = []
    def add(self, x):
        self.data.append(x)
    def addtwice(self, x):
        self.add(x)
        self.add(x)

Yöntemler sıradan işlevlerin yaptığı şekilde global değişkenlere başvuru yapabilir. Bir yönteme ilişkin global etki alanı sınıf tanımının bulunduğu modüldür. Sınıfın kendisi asla global etki alanı olarak kullanılmaz. Bir yöntem içinde global veri kullanmak için ender olarak iyi bir sebep olduğu halde, global etki alanı kullanımın pek çok mantıklı sebebi vardır. Örneğin, global etki alanına yüklenmiş işlev ve modülleri, yöntemler ve bunun içinde tanımlanmış diğer işlev ve sınıflar kullanılabilir. Genellikle yöntemi içeren sınıfın kendisi bu global etki alanı içinde tanımlanmıştır ve bir sonraki kısımda bir yöntemin kendi sınıfına başvurmak istemesi için birkaç iyi sebep bulacağız!

9.5. Kalıtım

Tabii ki, kalıtım desteği olmayan bir “sınıf” adına layık olmaz. Türetilmiş sınıf tanımının sözdizimi aşağıdaki gibidir:

class turemisSinifAdi(TemelSinifAdi):
    <deyim-1>
    .
    .
    .
    <deyim-N>

, TemelSinifAdi ismi türetilmiş sınıfın tanımının bulunduğu etki alanında tanımlı olmalıdır. Temel sınıf adı yerine bir ifade kullanmak da mümkündür. Bu temel sınıf adı başka bir modül içinde tanımlı olduğunda yararlıdır:

class turemisSinifAdi(modulAdi.TemelSinifAdi):

Bir türetilmiş sınıf tanımının işletilmesi bir temel sınıf ile aynıdır. Bir sınıf nesnesi yaratıldığında temel sınıf hatırlanır. Bu özellik başvurularını çözümlemede kullanılır; başvuruda bulunulan öznitelik o sınıfta yok ise temel sınıfta aranır. Bu kural temel sınıfın kendisi de başka bir sınıftan türetildiyse ardışık olarak çağırılır.

Türetilmiş sınıftan nesne oluşturmanın özel bir tarafı yoktur: turetilmisSinifAdi() o sınıfın yeni bir nesnesini yaratır. Yöntem başvuruları şu şekilde çözümlenirler: ilgili sınıf özelliği, gerekirse temel sınıflar zinciri taranarak, aranır ve yöntem başvurusu geçerli ise bu bir işlev verir.

Türetilmiş sınıflar temel sınıflarının yöntemlerini bastırabilir. Yöntemler aynı nesnenin diğer yöntemlerini çağırırken özel önceliklere sahip olmadıkları için aynı temel sınıfta tanımlı bir yöntemi çağıran temel sınıf yöntemi bunu bastıran bir türetilmiş sınıf yöntemini çağırmış olabilir. C++ yazılımcıları için not: Tüm Python yöntemleri sanaldır (virtual).

Türetilmiş sınıftaki bir bastıran yöntem aslında temel sınıftaki yöntemin yerini almak yerine onu geliştirmek isteyebilir. Temel sınıf yöntemini doğrudan çağırmanın basit bir yolu vardır: temelSinifadi.yontemAdi(self, argumanlar). Bu bazen istemciler için de faydalıdır. Bunun sadece, temel sınıf, global etki alanı içine doğrudan yüklendiyse çalıştığına dikkat edin.

9.5.1. Çoklu Kalıtım

Python çoklu kalıtımın kısıtlı bir şeklini destekler. Birçok temel sınıfı olan bir sınıf tanımı aşağıdaki gibidir:

class turemisSinifAdi(temel1, temel2, temel3):
    <ifade-1>
    .
    .
    .
    <ifade-N>

Burada sınıf özniteliği başvurularını çözümlemede kullanılan kuralı açıklamamız gerekiyor. Bir öznitelik turemisSinifAdi içinde bulunamazsa temel1 içinde sonra temel1'in temel sınıfları içerisinde ve burada da bulunamazsa temel2 içinde aranır vs.

Bazı kişilere temel1'den önce temel2 ve temel3 içinde arama yapmak daha doğal gelir. Bu temel1'in herhangi bir özniteliğinin temel1 içinde veya bunun temel sınıflarında tanımlanmış olup olmadığını bilmenizi gerektir ki temel2 içindeki isimler ile çakışmalardan kaçınabilesiniz.

Python'un kazara oluşan isim çakışmalarına karşı usule dayanması çoklu kalıtımın rasgele kullanımı yazılımın bakımını yapan için bir kabus olduğu açıktır. Çoklu kalıtımın iyi bilinen bir problemi aynı temel sınıfa sahip iki sınıftan türetme yapmaktır. Bu durumda ne olduğunu anlamak kolaydır; ancak bunun ne işe yarayacağı pek açık değildir.

9.6. Özel Değişkenler

Sınıfa özel belirteçler (identifier) için sınırlı destek vardır. __spam formundaki (en az iki alt çizgilik bir önek ve en fazla bir alt çizgilik sonek) bir belirteç _sinifadi__spam şeklini alır. Burada sinifadi o anki sınıf adının sonek alt çizgileri atılmış olan halidir. Bu değişiklik belirtecin sözdizimsel konumuna bakılmaksızın yapılır ki bu sınıf üyesine özel değişkenler yaratılabilsin. Değiştirilen belirteç 255 karakteri aşarsa kırpılabilir. Sınıflar dışında veya sınıf adı sadece alt çizgilerden oluşuyorsa kırpma olmaz.

İsim değiştirmenin amacı sınıflara, türemiş sınıflarca tanımlanan nesne değişkenlerini dert etmeden veya sınıf dışındaki nesne değişkenleri ile uğraşmadan, kolayca özel nesne değişkenleri ve yöntemleri tanımlama yolu sağlamaktır. Değiştirme kurallarının genelde kazaları önlemeye yönelik olduğuna dikkat edin; ancak yine de buna niyet eden kişi özel değişkenlere ulaşıp bunları değiştirebilir. Bu bazı özel durumlarda kullanışlı da olabilir.

exec(), eval() veya evalfile() işlevlerine aktarılacak kod, çağıran sınıf adının o anki sınıf adı olduğunu düşünmez; bu da “ikilik derlenmiş” kod ile sınırlı global deyiminin etkisine benzer. Aynı kısıtlama getattr(), setattr() ve delattr() işlevleri için ve doğrudan başvurulduğunda __dict__ için de mevcuttur.

9.7. Sona Kalanlar

Bazan isimli veri öğelerini bir arada paketlemek, Pascal kayıtları ya da C veri yapılarına benzer bir veri türü oluşturmak kullanışlı olabilir. Bir boş sınıf ile bu yapılabilir:

class Eleman:
    pass

ali = Eleman() # Boş bir eleman kaydı yarat

# Kaydın alanlarını doldur
ali.isim = 'Ali Veli'
ali.bolum = 'Muhasebe'
ali.maas = 1000000

Soyut bir veri türü bekleyen Python koduna o veri türünün yöntemlerini taklit eden bir sınıf geçirilebilir. Örneğin bir dosya nesnesinden bir miktar veriyi biçimleyen bir işleviniz varsa, read() ve readline() yöntemleri olan ve veriyi bir dizgeden alan bir sınıfı o işleve bağımsız değişken olarak aktarabilirsiniz.

Yöntem nesnelerinin de öznitelikleri vardır: m.im_self kendinin tanımlı olduğu nesneyi çağıran bir yöntem nesnesidir ve m.im_func ise kendini oluşturan işlevi çağıran bir yöntem nesnesidir.

9.8. İstisnalar Sınıf Olabilir

Kullanıcı tanımlı istisnalar artık dizge olmakla sınırlı değiller; sınıf da olabilir. Bu mekanizmayı kullanarak genişletilebilir istisna hiyerarşileri yaratılabilir.

raise deyimi için iki yeni biçem mevcut:

raise Sinif, gercekleme

raise gercekleme

İlk biçemde gercekleme Sinifa ait bir gerçekleme olmalıdır. İkinci biçem ise şunun kısaltmasıdır:

raise gercekleme.__class__, gercekleme

Bir except bloğu hem sınıflar hem de dizgeleri içerebilir. Bir except bloğu içindeki sınıf eğer aynı sınıf veya bir temel sınıf ise istisna ile uyumludur. Türetilmiş sınıf içeren bir except bloğu temel sınıf ile uyumlu değildir. Örneğin aşağıdaki yazılım B, C, D çıktısını o sırayla verir:

class B:
    pass
class C(B):
    pass
class D(C):
    pass
for c in [B, C, D]:
    try:
        raise c()
    except D:
        print "D"
    except C:
        print "C"
    except B:
        print "B"

Eğer except blokları ters sırayla yazılmış olsalardı (except B başta olacak şekilde) çıktı B, B, B olacaktı; çünkü uyan ilk except B bloğu tetiklenecekti.

Ele alınmamış sınıf istisnası için bir ileti yazılacağı zaman, önce sınıf adı yazılır, ardından iki nokta üst üste ve bir boşluk ve son olarak da gerçeklemenin yerleşik str() işlevinden geri döndürülen dizgenin karşılığı yazılır.



[71] Buna bir istisna: modül nesnelerinin, modülün isim alanını oluşturmada kullanılan sözlüğü oluşturan __dict__ isimli salt okunur ve gizli bir özniteliği vardır. __dict__ bir özniteliktir fakat global bir isim değildir.

[72] [Ç.N.]: Diğer nesne yönelimli yazılımlama dillerinde bu işleme "nesnenin ilklendirilmesi" denir.