9. Sınıflar
- 9.1. Terminoloji hakkında...
- 9.2. Python Etki ve İsim Alanları
- 9.3. Sınıflara İlk Bakış
-
- 9.3.1. Sınıf Tanımlama
- 9.3.2. Sınıf Nesneleri
- 9.3.3. Örnek Nesneler
- 9.3.4. Yöntem Nesneleri
- 9.4. Bazı Açıklamalar
- 9.5. Kalıtım
-
- 9.5.1. Çoklu Kalıtım
- 9.6. Özel Değişkenler
- 9.7. Sona Kalanlar
- 9.8. İstisnalar Sınıf Olabilir
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 Sinif
a 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.