Python her ne kadar fonksiyonel bir dil olsa da var olduğundan beri nesne yönelimli bir dil olmuştur. Sınıf oluşturma ve nesne yaratma gibi temel işlemler son derece kolaydır. Bu bölüm Python’ın nesne yönelimli taraflarını anlatmaya ayrılmıştır.Nesne Yönelimli Programlama (Object-Oriented Programming – OOP) Bilgisayar Mühendisliği Bölümlerinin en temel derslerinden biridir. Bu dersin tüm konularını sadece bu bölümü sıkıştırmak çok zordur. Bu bölümde en temel konular basit örneklerle açıklanacaktır. Bilginiz yok ise yazı içinde anahtar kelimeleri kullanarak konu hakkında daha fazla bilgi bulabilirsiniz. Öncelikle bir kaç terminolojik tanım vererek bazı temel konuları açıklayalım.
- Sınıf (Class): Sınıfın herhangi bir nesnesini karakterize eden bir özellik kümesi tanımlayan bir nesne için kullanıcı tanımlı bir prototiptir. Sınıf özellikleri üyelere, değişkenlere ve metotlara sahiptir. İyi bir sınıf tasarımı için yazılımcının soyutlama (abstraction) işlemini çok iyi yapması gerekir. Sınıf aslında yazılımcının kendi dünyasıdır.
- Sınıf değişkeni (Class variable): Bir sınıfın tüm örnekleri tarafından paylaşılan bir değişkendir. Sınıf değişkenleri bir sınıf içerisinde ancak sınıfın herhangi bir metodunun dışında tanımlanır.
- Veri üyesi (Data member): Bir sınıf ve nesnelerle ilişkili verileri tutan bir sınıf değişkeni veya örnek değişkenidir.
- Fonksiyon aşırı yüklenmesi (Function overloading) – Belirli bir fonksiyona birden fazla davranışın atanmasıdır. Yapılan işlem, ilgili nesne veya argüman türlerine göre değişir.
- Örnek değişkeni (Instance variable): Bir yöntem içinde tanımlanan ve yalnızca bir sınıfın geçerli örneğine ait olan bir değişkendir.
- Kalıtım (Inheritance): Bir sınıfın özelliklerinin, ondan türetilen diğer sınıflara aktarılmasıdır.
- Örnek (Instance): Belirli bir sınıfın bireysel bir nesnesidir. Örneğin, bir Circle sınıfına ait bir nesnedir. Başka bir deyişle Circle sınıfının bir örneğidir.
- Örnekleme (Instantiation): Bir sınıfın örneğinin oluşturulmasıdır.
- Metot (Method): Sınıf tanımlanırken tanımlanan özel bir fonksiyon türüdür.
- Nesne (Object): Sınıfı tarafından tanımlanan bir veri yapısının benzersiz bir örneğidir. Bir nesne, hem veri üyelerini (sınıf değişkenleri ve örnek değişkenleri) ve yöntemleri içerir.
- Operatörlerin aşırı yüklenmesi (Operator overloading): Belirli bir operatöre birden fazla işlevin atanmasıdır.
12.1. Sınıf Oluşturma
class ifadesi yeni bir sınıf tanımı oluşturur. Sözdizimi:
1 2 3 |
class ClassAdi: 'Seçimlik kısım: sınıf dokümasyon metni' class_kismi |
- Python kodu içinden ClassAdi.__doc__ ile ulaşabildiğimiz sınıf hakkında bir açıklama metnine sahiptir. Bu kısım seçimliktir.
- class_kismi sınıf üyelerini, veri özelliklerini ve fonksiyonları içerir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Ogrenci: 'Tüm öğrenciler için temel sınıf' Ogrenci_sayisi = 0 def __init__(self, adi, numarasi): self.adi = adi self.numarasi = numarasi Ogrenci.Ogrenci_sayisi += 1 def OgrenciSayisiGoster(self): print("Toplam öğrenci: %d" % Ogrenci.Ogrenci_sayisi) def OgrenciGoster(self): print("Adi : ", self.adi, ", Numarası: ", self.numarasi) |
Her programlama dilinin kendine özgü özellikleri vardır. Bu bölümde .Net ve Java ile kıyaslamalar yapılacaktır.
- Ogrenci_sayisi değişkeni bu sınıfın tüm örnekleri (nesneleri) arasında paylaşılan değer olarak bir sınıf değişkenidir. Sınıf ismi.değişken ismi şeklinde (bu örnek için Ogrenci.Ogrenci_sayisi şeklinde) sınıf dışından ve sınıf içinden ulaşabiliriz. .Net ve Java’daki statik değişken kavramına benzemektedir. Ancak, bu programlama dillerinde static ifadesi belirtilmelidir. Python programlama dilinde ise böyle bir özelliğine ihtiyaç yoktur.
- __init__() – (başlangıç ve son iki alt çizgili) yapıcı (constructor) olarak bilinen sınıf ilk oluşturulan yapılacak işlemlerin tanımlandığı özel bir metottur. Python, bu sınıfın nesne/örnek oluştururken oluştururken çağrılan ilk metottur.
- Normal bir fonksiyon tanımlar gibi sınıf metotlarını tanımlanabilir.
- Sınıftaki tüm metotlar için self ilk argüman olarak kullanılır. self nesne üzerinden ulaşacağımız anlamına gelir. Python sizin için self argümanları listeye ekler; metotları çağırdığınızda bunu eklemeniz gerekmez.
12.2. Sınıftan bir nesne – örnek oluşturma ve erişim
Bir sınıfın örneklerini oluşturmak için, sınıf adını kullanarak sınıfı çağırırsınız ve __init__ yönteminin kabul ettiği argümanları iletirsiniz.
1 2 3 |
ogr1 = Ogrenci("Öğrenci1", 1000) ogr2 = Ogrenci("Öğrenci2", 2000) ogr3 = Ogrenci("Öğrenci3", 3000) |
.Net ve Java gibi dillerde yeni bir örnek oluşturmak için new anahtar kelimesi kullanılır. Ancak, Python’da direkt atama işlemi ve sınıf ismi çağrılır.
Nesnenin özelliklerine ulaşmak için nokta operatörü ile nesne içindeki özelliklere erişebiliriz. Örneğin,
1 2 3 4 |
ogr1.OgrenciGoster() ogr2.OgrenciGoster() ogr3.OgrenciGoster() print("Toplam öğrenci %d" % Ogrenci.Ogrenci_sayisi) |
1 2 3 4 |
Adi : Öğrenci1 , Numarası: 1000 Adi : Öğrenci2 , Numarası: 2000 Adi : Öğrenci3 , Numarası: 3000 Toplam öğrenci 3 |
12.3. Örnek oluşturduktan nitelik kontrolü
Herhangi bir zamanda sınıfların ve nesnelerin niteliklerini ekleyebilir, kaldırabilir veya değiştirebilir.
1 2 3 4 5 6 |
ogr1.yas = 15 #ilk defa değişken oluşturma print(ogr1.yas) ogr1.yas = 16 #değişkeni değiştirme print(ogr1.yas) del ogr1.yas #değişkeni bellekten çıkarma print(ogr1.yas) |
1 2 3 4 5 6 7 8 9 |
15 16 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-7-8c55e8e9bf8b> in <module>() 1 del ogr1.yas ----> 2 print(ogr1.yas) AttributeError: 'Ogrenci' object has no attribute 'yas' |
Özelliklere erişmek için normal ifadeleri kullanmak yerine, aşağıdaki fonksiyonları kullanabilirsiniz:
- getattr (obj, name [, default]): nesnenin özelliğine erişmek için kullanılır.
- hasattr (obj, name): bir özellik var olup olmadığını kontrol etmek için kullanılır. (True / False)
- setattr (obj, name, value) – bir özellik belirlemek için kullanılır. Özellik yoksa, o zaman yaratılır.
- delattr (obj, name) – bir özelliği silmek için kullanılır.
12.4. Yerleşik sınıf özellikleri
Her Python sınıfı yerleşik özellikleri takip eder ve herhangi bir özellik gibi nokta operatörünü kullanarak erişilebilir.
- __dict__ : Sınıfın ad alanını içeren sözlük.
- __doc__ : Tanımlanmamışsa, sınıf belgelendirme metni içerir.
- __name__ : Sınıf adı.
- __module__ : Sınıfın tanımlandığı modül adı. Bu özellik, etkileşimli modda “__main__” şeklindedir.
- __bases__ : Temel sınıf listesindeki oluşum sırasına göre, temel sınıflarda muhtemelen boş bir tüpledir.
1 2 3 4 5 |
print("Ogrenci.__doc__:", Ogrenci.__doc__) print("Ogrenci.__name__:", Ogrenci.__name__) print("Ogrenci.__module__:", Ogrenci.__module__) print("Ogrenci.__bases__:", Ogrenci.__bases__) print("Ogrenci.__dict__:", Ogrenci.__dict__) |
1 2 3 4 5 |
Ogrenci.__doc__: Tüm öğrenciler için temel sınıf Ogrenci.__name__: Ogrenci Ogrenci.__module__: __main__ Ogrenci.__bases__: (<class 'object'>,) Ogrenci.__dict__: {'__module__': '__main__', '__doc__': 'Tüm öğrenciler için temel sınıf', 'Ogrenci_sayisi': 3, '__init__': <function Ogrenci.__init__ at 0x000001E5501B7AE8>, 'OgrenciSayisiGoster': <function Ogrenci.OgrenciSayisiGoster at 0x000001E5501B76A8>, 'OgrenciGoster': <function Ogrenci.OgrenciGoster at 0x000001E5501B7950>, '__dict__': <attribute '__dict__' of 'Ogrenci' objects>, '__weakref__': <attribute '__weakref__' of 'Ogrenci' objects>} |
12.5. Nesneleri yok etme – Garbage collection
Bir nesne yönelimli dilin en önemli özelliği belleği dinamik olarak kullanmasıdır. Python, bellek alanınındaki gereksiz nesneleri (yerleşik tipler veya sınıf örnekleri) otomatik olarak siler. .Net ve Java’da olduğu gibi Python’un periyodik olarak kullanımda olmayan bellek bloklarını sildiği süreç Garbage collection (Çöp Toplama) olarak adlandırılır. Python’un çöp toplayıcısı, program yürütme sırasında çalışır ve bir nesnenin referans sayısı sıfıra ulaştığında tetiklenir. Bir nesnenin referans sayısı, kendisine işaret eden diğer adların sayısı değiştiğinde değişir. Bir nesnenin referans sayısı, yeni bir ad verildiğinde veya bir konteynere (liste, tuple veya sözlük) yerleştirildiğinde artar. Nesnenin referans sayısı del ile silindiğinde azalır, referansı yeniden atar veya referans kapsam dışı kalır. Bir nesnenin referans sayısı sıfıra ulaştığında, Python otomatik olarak toplar.
1 2 3 4 5 6 7 |
a = 40 # Nesne yaratma <40> b = a # <40>'ın ref sayısı artar c = [b] # <40>'ın ref sayısı artar del a # <40>'ın ref sayısı azalır b = 100 # <40>'ın ref sayısı azalır c[0] = -1 # <40>'ın ref sayısı azalır |
Normalde bir çöp toplayıcının bellekte kullanılmayan blokları geri aldığını fark etmezsiniz. Sınıflara destructor (yıkıcı) denilen bir metotla yıkılma işlemi sırasında çalışan özel bir metot yazılabilir. Örneğin,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Nokta: def __init__( self, x=0, y=0): self.x = x self.y = y def __del__(self): class_name = self.__class__.__name__ print(class_name, "yok edildi!!!") pt1 = Nokta() pt2 = pt1 pt3 = pt1 print(id(pt1), id(pt2), id(pt3)) # nesnelerin id'leri del pt1 del pt2 del pt3 |
1 2 |
2084403848696 2084403848696 2084403848696 Nokta yok edildi!!! |
Üç nesne adres olarak birbirine bağlanmış ve üç nesnede aynı adresi gösteriyor. Ardından tüm referanslar silinince destructor devreye girer.
12.6. Kalıtım (Inheritance)
Nesne yönelimli programlama gerçek dünyanın programlamaya arttırılma çabası olarak görülebilir. Gerçek dünyada sınıflar ve sınıflardan oluşmuş nesneler vardır. “Ol” denir, nesneler bilgisayar belleğinde oluşur. Gerçek dünya sınıflar ve sınıflar arasında ilişkiler mevcuttur. Kalıtım, sınıf ve sınıfı kullanan alt sınıflar olarak görülebilir. Alt sınıflar, üst sınıfın özelliklerini devralır ve bu özellikleri alt sınıfta tanımlanmış gibi kullanır. Hatta, bir alt sınıfta istenilen özellikler geçersiz kılınabilir. Sözdizimi:
1 2 3 |
class AltClassAdi (AnaClass1[, AnaClass2, ...]): 'Seçimlik: sınıf açıklama metni' class_suite |
Türetilmiş sınıflar, ana sınıflarına benzer şekilde beyan edilirler; ancak, sınıf adından sonra miras almak için temel sınıfların bir listesi verilir. Alt sınıflar fonksiyon parametresi gibi parantez içinde yazılır. Java’da extends ve .Net ise “:”‘den sonra alt sınıflar verilirken Python daha farklı bir kullanım söz konusudur. 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 22 23 24 25 |
class Ana: # ana sinif AnaAttr = 100 def __init__(self): print("Ana constructor'ı çağırma") def AnaMethod(self): print('Ana metotu çağırma') def setAttr(self, attr): Ana.AnaAttr = attr def getAttr(self): print('Ana attribute :', Ana.AnaAttr) class Cocuk(Ana): # cocuk sinif def __init__(self): print("Cocuk constructor'ı çağırma") def CocukMethod(self): print('Cocuk methotu çağırma') c = Cocuk() # Cocuk ornegi c.CocukMethod() # Cocuk metodu cagirma c.AnaMethod() # Ana'nin metodunu cagirma c.setAttr(200) # Ana'nin metodunu set icin cagirma c.getAttr() # Ana'nin metodunu get icin cagirma |
1 2 3 4 |
Cocuk constructor'ı çağırma Cocuk methotu çağırma Ana metotu çağırma Ana attribute : 200 |
Benzer bir yolla, birden fazla sınıfı bir sınıfta kalıtım için kullanabilirsiniz. .Net ve Java’da bir sınıf için bir base sınıf kullanılabiliyordu, ancak Python bu konuda esnekliğini gösterir. Örneğin,
1 2 3 4 5 6 7 8 |
class A: # Ana sinif 1 ..... class B: # Ana sinif 2 ..... class C(A, B): # A and B'nin alt sinifi ..... |
İki sınıf ve nesnelerin ilişkilerini kontrol etmek için issclass () veya isinstance () fonksiyonlarını kullanabilirsiniz.
- issubclass (alt_sinif, ana_sinif) eğer sınıflar arası bir ilişki var ise fonksiyon True değerini döndürür.
- Nesnenin bir sınıfla ilişkisi olup olmadığını bulmak için isinstance (obj:nesne, Class:Sınıf) fonksiyonu true değerini döndürür.
12.7. Geçersiz kılma (Overriding)
Ana sınıftaki metotlarımızı her zaman geçersiz kılabilirsiniz. Ana sınıftaki metodu geçersiz kılmak için alt sınıfta aynı isimle bir fonksiyon yaratmamız yeterlidir. Örneğin,
1 2 3 4 5 6 7 8 9 10 |
class Ana: # Ana sinif tanimlama def myMethod(self): print('Ana sınıf içindeki metodu çağırma') class Cocuk(Ana): # Cocuk sinif tanimlama def myMethod(self): print('Cocuk sınıf içindeki metodu çağırma') c = Cocuk() # Bir cocuk örnegi c.myMethod() # Cocuk overridden edilmis metodu cagiriyor |
1 |
Cocuk sınıf içindeki metodu çağırma |
12.8. Temel aşırı yükleme (overloading) yöntemleri
Aşağıdaki tabloda kendi sınıflarınızda geçersiz kılınabilecek bazı genel metotları listelenmektedir.
Metot | Tanım ve Basit çağırma |
---|---|
__init__ ( self [,args…] ) | Yapıcı – Constructor (seçimlik argüman sayısı): Bir nesne oluşurken ilk istekleri Basit çağırma : obj = className(args) |
__del__( self ) | Yıkıcı – Destructor: Bir nesnenin ölüm anında son istekleri Basit çağırma : del obj |
__repr__( self ) | Değerlendirilen katar gösterimi Basit çağırma : repr(obj) |
__str__( self ) | Yazdırılabilir katar gösterimi Basit çağırma : str(obj) |
12.9. Operatörleri aşırı yükleme (Operator Overloading)
“+” karakteri iki string olunca birleştirme yaparken sayısal değerler olunca ise toplama işlemi yapar. Bu durumda + operatörünün duruma göre farklı davrandığı sonucu ortaya çıkar. Bu konuya operatörlerin aşırı yüklenmesi denir. Konuyu bir örnekle açıklayalım. x ve y özelliklerine sahip Vektor adında bir sınıfımız olsun. Bu vektörden iki nesne oluşturup + işareti koyarsak ne olur. Deneyelim.
1 2 3 4 5 6 7 8 9 10 11 |
class Vektor: def __init__(self, x, y): self.x = x self.y = y def __str__(self): return 'Vektör (%d, %d)' % (self.x, self.y) v1 = Vektor(2,10) v2 = Vektor(5,-2) print(v1 + v2) |
1 2 3 4 5 6 7 8 |
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-17-bde1bd003429> in <module>() 9 v1 = Vektor(2,10) 10 v2 = Vektor(5,-2) ---> 11 print(v1 + v2) TypeError: unsupported operand type(s) for +: 'Vektor' and 'Vektor' |
“+” sınıfta nasıl kullanılacağına dair bir bilgi olmadığı için toplama işlemini yapamadı. Python bunun için __add__ metodu sunar. __add__ metodu içine + işleminin nasıl davranacağını belirleyebiliriz. Yukarıdaki sınıf içine sadece aşağıdaki satırları ekleyelim.
1 2 3 |
#...sınıf... def __add__(self,other): return Vektor(self.x + other.x, self.y + other.y) |
1 |
Vektör (7, 8) |
self’i birinci nesne ve other’ı da ikinci nesnedir. Bu iki nesne arasına “+” işareti gelince yeni bir Vektor objesi döndürülmektedir. print metodu içinde ise Vektor sınıfının __str___ metodu devreye girmektedir.
12.9. Veri gizleme
En temel erişim belirteçleri .Net ve Java için public, private ve protected ifadeleridir. Ancak, Python’da klasik bir değişken public konumunda iken değişkenin sol tarafına konan çift alt çizgi ile private özelliğini barındırır. Ayrıca, Python diğer programlama dillerinde olduğu gibi özel ifadelere ihtiyaç duymaz. Çift alt çizgili değişken sadece sınıf içinden görülebilir ve dışarıdan görülebilir değildir. Basit bir örnek ile açıklayalım.
1 2 3 4 5 6 7 8 9 10 11 |
class Sayac: __priSayac = 0 def say(self): self.__priSayac += 1 print(self.__priSayac) syc = Sayac() syc.say() syc.say() print(syc.__priSayac) |
1 2 3 4 5 6 7 8 9 10 |
1 2 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-24-83d1e1612d9b> in <module>() 1 syc.say() ----> 2 print(syc.__priSayac) AttributeError: 'Sayac' object has no attribute '__priSayac' |
Burada “__priSayac” değişkeni sınıf dışından ulaşılmayan bir değişkendir. Ancak, “say” metodu ile sınıf içinden bu değişkene ulaşabiliriz.
Protected için tek alt çizgi “_” kullanılır.