Python, çok yönlü bir programlama dilidir ve farklı programlama paradigmalarını destekler. Bu paradigmalardan en önemlilerinden ve modern yazılım geliştirmede yaygın olarak kullanılanı Nesne Yönelimli Programlama (Object-Oriented Programming — OOP)’dir. OOP, programları tek bir uzun komut dizisi yerine, birbirleriyle etkileşim halinde olan “nesneler” topluluğu olarak modellemeye odaklanan bir yaklaşımdır.
OOP’nin temel yapı taşları sınıflar (classes) ve nesneler (objects)’dir. Sınıflar, nesnelerin nasıl görüneceğini ve nasıl davranacağını tanımlayan şablonlar veya planlarken; nesneler, bu sınıflardan oluşturulan somut örneklerdir. Bu yaklaşım, kodu daha organize, modüler, yeniden kullanılabilir ve bakımı kolay hale getirir.
Bu kapsamlı rehberde, Python’da OOP’nin temellerini atacağız. Sınıfların ve nesnelerin ne olduğunu, nasıl tanımlanıp kullanılacağını, nesnelerin başlangıç durumunu ayarlayan __init__
metodunu, nesne verilerine ve davranışlarına erişimi sağlayan self
parametresini, sınıf ve örnek niteliklerini (attributes) ve metotlarını (methods) detaylıca inceleyeceğiz. Ayrıca, OOP’nin temel prensipleri olan kapsülleme (encapsulation), kalıtım (inheritance) ve çok biçimliliğe (polymorphism) Python özelinde bir giriş yapacağız.
Bölüm 1: Nesne Yönelimli Programlama (OOP) Nedir?
OOP’yi anlamak için gerçek dünyadan bir analoji kullanabiliriz. Bir araba fabrikasını düşünün:
Sınıf (Class): Fabrikadaki araba tasarım planı veya kalıbıdır. Bu plan, bir arabanın sahip olması gereken özellikleri (renk, model, motor gücü, kapı sayısı — bunlar niteliklerdir (attributes)) ve yapabileceği eylemleri (hızlanma, fren yapma, korna çalma — bunlar metotlardır (methods)) tanımlar. Planın kendisi somut bir araba değildir.
Nesne (Object/Instance): Bu plana göre üretilen her bir gerçek arabadır. Her araba (nesne) aynı plana (sınıfa) göre yapılmış olsa da, kendi özel nitelik değerlerine (kırmızı renk, sedan model, 150 beygir vb.) sahip olabilir ve planın tanımladığı eylemleri (metotları) gerçekleştirebilir. Fabrikadan çıkan her araba, Araba
sınıfının bir örneğidir (instance).
OOP’nin temel fikirleri şunlardır:
Nesneler (Objects): Programdaki temel varlıklardır. Veri (nitelikler) ve bu veriyi işleyen davranışları (metotlar) bir arada tutarlar.
Sınıflar (Classes): Nesnelerin şablonlarıdır. Belirli bir türdeki nesnelerin sahip olacağı nitelikleri ve metotları tanımlarlar.
Kapsülleme (Encapsulation): Bir nesnenin verilerini (niteliklerini) ve bu veriler üzerinde işlem yapan metotlarını tek bir birim (sınıf) içinde paketleme fikridir. Ayrıca, nesnenin iç detaylarını gizleyip sadece gerekli arayüzleri dışarıya sunmayı hedefler (information hiding).
Kalıtım (Inheritance): Bir sınıfın (alt sınıf/derived class) başka bir sınıfın (üst sınıf/base class) özelliklerini ve davranışlarını miras almasını sağlar. Bu, kod tekrarını azaltır ve hiyerarşik ilişkiler kurmayı kolaylaştırır (“is-a” ilişkisi, örn: “Köpek bir Hayvan’dır”).
Çok Biçimlilik (Polymorphism): “Çok biçimli” anlamına gelir. Farklı sınıflardan nesnelerin aynı isimdeki metotlara farklı şekillerde yanıt verebilmesini ifade eder. Bu, daha esnek ve genişletilebilir kod yazmayı sağlar.
Python, bu OOP prensiplerini destekleyen güçlü bir dildir.
Bölüm 2: Sınıf Tanımlama (class
Anahtar Kelimesi)
Python’da bir sınıf tanımlamak için class anahtar kelimesi kullanılır, ardından sınıfın adı (genellikle Büyük Harfle BaşlayanCamelCase isimlendirme standardı - PEP 8) ve iki nokta üst üste (:) gelir. Sınıfın gövdesi girintili olarak yazılır.
En basit sınıf tanımı:
class BenimIlkSinifim:
pass # pass ifadesi, bloğun boş olduğunu belirtir, hiçbir şey yapmaz.
# Sözdizimsel olarak bir blok gerektiğinde kullanılır.
Bu sınıf henüz hiçbir nitelik veya metoda sahip değildir, sadece boş bir şablondur.
2.1. Sınıf Nitelikleri (Class Attributes)
Sınıf nitelikleri, doğrudan sınıf tanımının içinde (herhangi bir metodun dışında) tanımlanan değişkenlerdir. Bu nitelikler, o sınıftan oluşturulan tüm nesneler (örnekler) tarafından paylaşılır. Sınıfın kendisine aittirler.
class Kopek:
# Bu bir sınıf niteliğidir (tüm köpek nesneleri için aynı)
tur = "Canis familiaris" # Latince köpek türü
def init(self, isim, yas):
# Bunlar örnek nitelikleridir (her köpek nesnesi için farklı olabilir)
self.isim = isim
self.yas = yas
Sınıf niteliğine sınıf adı üzerinden erişim
print(f"Köpek sınıfının türü: {Kopek.tur}")
Nesneler oluşturduğumuzda da sınıf niteliğine erişebilirler
kopek1 = Kopek("Karabaş", 3)
kopek2 = Kopek("Boncuk", 5)
print(f"{kopek1.isim}'in türü: {kopek1.tur}") # Nesne üzerinden de erişilebilir
print(f"{kopek2.isim}'in türü: {kopek2.tur}")
Sınıf niteliğini sınıf üzerinden değiştirirsek, tüm nesneleri etkiler
Kopek.tur = "Evcil Hayvan"
print(f"Değişim sonrası {kopek1.isim}'in türü: {kopek1.tur}")
Sınıf nitelikleri genellikle sabit değerler veya tüm örnekler için ortak olan varsayılan durumlar için kullanılır.
2.2. Metotlar (Methods)
Sınıf içinde tanımlanan fonksiyonlara metot denir. Metotlar, sınıfın nesnelerinin sahip olacağı davranışları tanımlar. Sınıf içindeki normal fonksiyon tanımlarına benzerler, ancak önemli bir farkları vardır: genellikle ilk parametre olarak self adında özel bir parametre alırlar.
class Araba:
# Sınıf Niteliği
tekerlek_sayisi = 4
def init(self, marka, model, renk):
# Örnek Nitelikleri
self.marka = marka
self.model = model
self.renk = renk
self.hiz = 0 # Başlangıç hızı
# Bu bir örnek metodudur
def hizlan(self, miktar):
"""Arabanın hızını belirtilen miktar kadar artırır."""
self.hiz += miktar
print(f"{self.marka} {self.model} hızlandı. Yeni hız: {self.hiz} km/s")
# Başka bir örnek metodu
def fren_yap(self, miktar):
"""Arabanın hızını belirtilen miktar kadar azaltır."""
self.hiz -= miktar
if self.hiz < 0:
self.hiz = 0 # Hız negatif olamaz
print(f"{self.marka} {self.model} yavaşladı. Yeni hız: {self.hiz} km/s")
# Bilgileri gösteren bir metot
def bilgileri_goster(self):
"""Arabanın bilgilerini yazdırır."""
print(f"--- Araba Bilgileri ---")
print(f"Marka: {self.marka}")
print(f"Model: {self.model}")
print(f"Renk: {self.renk}")
print(f"Mevcut Hız: {self.hiz} km/s")
print(f"Tekerlek Sayısı: {self.tekerlek_sayisi}") # Sınıf niteliğine de erişebilir
hizlan, fren_yap ve bilgileri_goster bu sınıfın metotlarıdır. self parametresinin rolüne birazdan detaylı bakacağız.
Bölüm 3: Nesne Oluşturma (Instantiation) ve Kullanımı
Bir sınıf tanımladıktan sonra, o sınıftan somut nesneler (objects) veya örnekler (instances) oluşturabiliriz. Bu işleme örnekleme (instantiation) denir.
Nesne oluşturmak için sınıf adını bir fonksiyon gibi çağırırız:
Araba sınıfından nesneler oluşturalım
araba1 = Araba("Toyota", "Corolla", "Beyaz")
araba2 = Araba("Ford", "Mustang", "Kırmızı")
Artık araba1 ve araba2, Araba sınıfının farklı örnekleridir (nesneleridir)
print(f"araba1'in tipi: {type(araba1)}") #
print(f"araba2'nin tipi: {type(araba2)}") #
print(f"araba1 ve araba2 aynı nesne mi? {araba1 is araba2}") # False
3.1. Niteliklere Erişim (Attribute Access)
Bir nesnenin niteliklerine (hem sınıf hem de örnek niteliklerine) erişmek için nokta (.) notasyonu kullanılır: nesne_adı.nitelik_adı.
araba1 nesnesinin niteliklerine erişim
print(f"\nAraba 1 Detayları:")
print(f"Marka: {araba1.marka}") # Toyota
print(f"Model: {araba1.model}") # Corolla
print(f"Renk: {araba1.renk}") # Beyaz
print(f"Başlangıç Hızı: {araba1.hiz}") # 0
print(f"Tekerlek Sayısı: {araba1.tekerlek_sayisi}") # 4 (Sınıf niteliği)
araba2 nesnesinin niteliklerine erişim
print(f"\nAraba 2 Detayları:")
print(f"Marka: {araba2.marka}") # Ford
print(f"Renk: {araba2.renk}") # Kırmızı
Nesnelerin örnek nitelikleri (marka, model, hiz vb.) birbirlerinden bağımsızdır.
3.2. Metotları Çağırma (Method Invocation)
Bir nesnenin metotlarını çağırmak için yine nokta notasyonu kullanılır, ancak metot adından sonra parantez () eklenir: nesne_adı.metot_adı(argümanlar).
Metot çağrıldığında, Python otomatik olarak o nesnenin kendisini (yani araba1 veya araba2'yi) metodun ilk parametresi olan self yerine geçirir.
araba1 üzerinde metotları çağırma
print("\nAraba 1 Hareketleri:")
araba1.hizlan(50) # Python bunu Araba.hizlan(araba1, 50) gibi çalıştırır
araba1.hizlan(30)
araba1.fren_yap(20)
araba1.bilgileri_goster()
araba2 üzerinde metotları çağırma
print("\nAraba 2 Hareketleri:")
araba2.hizlan(100)
araba2.hizlan(60)
araba2.bilgileri_goster()
Her nesne kendi durumunu (hızını) korur ve metotlar bu duruma göre çalışır.
Bölüm 4: __init__
Metodu (Başlatıcı/Constructor)
OOP’de sıkça karşılaşılan bir ihtiyaç, bir nesne oluşturulduğunda ona başlangıç değerleri veya başlangıç durumu atamaktır. Python’da bu işlevi yerine getiren özel bir metot vardır: init (çift alt çizgi ile başlar ve biter, "dunder init" olarak okunur).
Amaç: Nesne oluşturulur oluşturulmaz (instantiation sırasında) otomatik olarak çağrılır. Temel görevi, nesnenin örnek niteliklerini (instance attributes) başlatmaktır.
self
Parametresi: Tıpkı diğer örnek metotları gibi, init metodunun da ilk parametresi her zaman self'tir. Bu, o an oluşturulmakta olan nesnenin kendisini temsil eder.
Diğer Parametreler: self'ten sonra, nesneyi oluştururken dışarıdan alınacak değerler için ek parametreler tanımlanabilir.
Çağrı Şekli: Biz init'i doğrudan çağırmayız. Araba("Toyota", "Corolla", "Beyaz") gibi sınıfı çağırdığımızda, Python otomatik olarak Araba.init(yeni_olusan_nesne, "Toyota", "Corolla", "Beyaz") şeklinde init metodunu çalıştırır.
Dönüş Değeri: init metodunun amacı nesneyi başlatmak olduğu için, açıkça bir değer döndürmemelidir (yani return ifadesiyle bir değer döndürmemelidir, aksi halde TypeError alınır). Dolaylı olarak None döndürür.
Araba sınıfımızdaki init metodunu tekrar inceleyelim:
class Araba:
# ... (sınıf niteliği) ...
def init(self, marka_param, model_param, renk_param):
print(f"Araba.init çağrıldı! ({marka_param})") # Ne zaman çağrıldığını görmek için
# Parametre olarak gelen değerleri, nesnenin örnek niteliklerine atıyoruz
# 'self.yeni_nitelik = deger' yapısı, nesneye ait bir nitelik oluşturur/atar.
self.marka = marka_param
self.model = model_param
self.renk = renk_param
self.hiz = 0 # Başlangıç hızı her zaman 0 olsun
# ... (diğer metotlar) ...
Nesne oluşturma (bu satır init'i tetikler)
yeni_araba = Araba("BMW", "X5", "Siyah")
Çıktı: Araba.init çağrıldı! (BMW)
init tarafından atanan niteliklere erişim
print(yeni_araba.marka) # BMW
print(yeni_araba.hiz) # 0
init, her nesnenin başlangıçta geçerli ve tutarlı bir durumda olmasını sağlamak için kritik öneme sahiptir.
Bölüm 5: self
Parametresi: Nesnenin Kendisine Referans
Python sınıflarında tanımlanan örnek metotlarının (init dahil) ilk parametresi geleneksel olarak self olarak adlandırılır. Peki bu self tam olarak nedir ve neden gereklidir?
Nesnenin Kendisi: self, metodun çağrıldığı nesnenin (örneğin) kendisini temsil eder. Bir metot içinde self kullanarak, o nesneye ait diğer niteliklere (self.nitelik_adi) ve metotlara (self.metot_adi()) erişebiliriz.
Otomatik Geçirilir: Bir nesne üzerinden metot çağırdığımızda (araba1.hizlan(50)), Python otomatik olarak araba1 nesnesini metodun ilk parametresi (self) olarak geçirir. Bizim çağrı sırasında self için ayrıca bir argüman vermemize gerek yoktur.
Zorunlu İlk Parametre (Kural): Örnek metotları tanımlarken, ilk parametre her zaman o örneğe referans olmalıdır. Bu parametrenin adının self olması sadece bir konvansiyondur (gelenektir), teknik olarak başka bir isim (örn: this, obj) de kullanılabilir. Ancak Python topluluğunda ezici bir çoğunlukla self kullanılır ve bu standarda uymak kodun okunabilirliği için çok önemlidir.
Araba.hizlan metodunu tekrar düşünelim:
def hizlan(self, miktar):
# 'self.hiz', bu 'hizlan' metodunun çağrıldığı
# belirli araba nesnesinin 'hiz' niteliğine erişir.
self.hiz += miktar
print(f"{self.marka} {self.model} hızlandı. Yeni hız: {self.hiz} km/s")
Eğer self olmasaydı, hizlan metodu hangi arabanın hızını artıracağını bilemezdi. self, metodun hangi nesne üzerinde çalıştığını bilmesini sağlar.
Bölüm 6: Örnek Nitelikleri vs. Sınıf Nitelikleri
Bu iki kavram arasındaki farkı netleştirmek önemlidir:
ÖzellikÖrnek Niteliği (Instance Attribute)Sınıf Niteliği (Class Attribute)Tanımlama YeriGenellikle init içinde self.nitelik = deger ileDoğrudan sınıf tanımı içinde (metotların dışında)Ait Olduğu YerSınıfın her bir nesnesine (örneğine) özeldir.Sınıfın kendisine aittir.PaylaşımHer nesnenin kendi kopyası vardır, değeri nesneden nesneye değişebilir.Sınıftan türetilen tüm nesneler tarafından paylaşılır.Erişimnesne.nitelikSinifAdi.nitelik veya nesne.nitelik (eğer nesnede aynı isimde bir örnek niteliği yoksa)Değiştirme Etkisinesne.nitelik = yeni_deger sadece o nesneyi etkiler.SinifAdi.nitelik = yeni_deger tüm nesneleri etkiler (eğer nesneler bu niteliği kendi üzerlerinde yeniden tanımlamamışsa). nesne.nitelik = yeni_deger ise sadece o nesne için aynı isimde bir örnek niteliği oluşturur, sınıf niteliğini değiştirmez.Kullanım AmacıHer nesnenin kendine özgü durumunu (state) saklamak (örn: arabanın rengi, hızı).Sınıfın tüm örnekleri için ortak olan sabitleri veya varsayılan değerleri tanımlamak (örn: Pi sayısı, bir türün bilimsel adı, varsayılan bir ayar).
class Daire:
# Sınıf Niteliği
pi = 3.14159
def init(self, yaricap):
# Örnek Niteliği
self.yaricap = yaricap
def alan_hesapla(self):
# Hem sınıf hem örnek niteliği kullanılıyor
return self.pi * (self.yaricap ** 2) # self.pi yerine Daire.pi de yazılabilirdi
d1 = Daire(5)
d2 = Daire(10)
print(f"d1 Alan: {d1.alan_hesapla()}")
print(f"d2 Alan: {d2.alan_hesapla()}")
print(f"d1 pi: {d1.pi}, d2 pi: {d2.pi}, Daire.pi: {Daire.pi}")
Sınıf niteliğini değiştirelim
Daire.pi = 3.14
print("\nPi değeri sınıf üzerinden değiştirildi.")
print(f"d1 pi: {d1.pi}, d2 pi: {d2.pi}, Daire.pi: {Daire.pi}")
print(f"d1 Yeni Alan: {d1.alan_hesapla()}") # Değişiklik yansıdı
d1 nesnesi için 'pi' adında bir ÖRNEK niteliği oluşturalım
d1.pi = 3.14159265 # Bu, Daire.pi'yi DEĞİŞTİRMEZ, d1'e özel pi oluşturur
print("\nd1 nesnesine özel 'pi' örnek niteliği atandı.")
print(f"d1 pi (örnek): {d1.pi}")
print(f"d2 pi (sınıf): {d2.pi}")
print(f"Daire.pi (sınıf): {Daire.pi}")
print(f"d1 Yeni Alan (örnek pi ile): {d1.alan_hesapla()}") # d1 artık kendi pi'sini kullanır
print(f"d2 Yeni Alan (sınıf pi ile): {d2.alan_hesapla()}")
Bölüm 7: Özel Metotlar (Dunder/Magic Methods)
Python sınıflarında, isimleri çift alt çizgi ile başlayıp biten (...) özel metotlar bulunur. Bunlara "dunder" (double underscore) metotları veya "sihirli" (magic) metotlar da denir. Bu metotlar, Python'un yerleşik fonksiyonları veya operatörleri ile sınıflarımızın etkileşime girmesini sağlar.
init, bu özel metotlardan biridir ve nesne oluşturulduğunda çağrılır. Bir diğeri sık kullanılan ise str metodudur.
__str__()
Metodu
Amaç: Nesnenin kullanıcı dostu, okunabilir bir string temsilini döndürmektir.
Ne Zaman Çağrılır: print(nesne) fonksiyonu çağrıldığında veya str(nesne) ile nesne string'e çevrilmeye çalışıldığında otomatik olarak çağrılır.
Dönüş Değeri: Bir string döndürmelidir.
Eğer str tanımlanmazsa, print(nesne) genellikle nesnenin bellek adresini içeren varsayılan, pek de anlamlı olmayan bir temsil (repr'dan gelen) gösterir.
class Kitap:
def init(self, ad, yazar, sayfa_sayisi):
self.ad = ad
self.yazar = yazar
self.sayfa_sayisi = sayfa_sayisi
# str metodu OLMADAN nesneyi yazdırma
kitap1 = Kitap("Sefiller", "Victor Hugo", 1463)
print(f"str olmadan: {kitap1}") # <main.Kitap object at 0x...> gibi bir çıktı verir
class KitapStr:
def init(self, ad, yazar, sayfa_sayisi):
self.ad = ad
self.yazar = yazar
self.sayfa_sayisi = sayfa_sayisi
def str(self):
# Okunabilir bir string döndürelim
return f"'{self.ad}' - {self.yazar} ({self.sayfa_sayisi} sayfa)"
kitap2 = KitapStr("Suç ve Ceza", "Dostoyevski", 671)
print(f"str ile: {kitap2}") # 'str' metodu çağrılır
Çıktı: str ile: 'Suç ve Ceza' - Dostoyevski (671 sayfa)
str() fonksiyonu da str'ü kullanır
kitap2_str = str(kitap2)
print(f"str(kitap2) sonucu: {kitap2_str}")
Diğer bazı yaygın özel metotlar şunlardır: repr (nesnenin geliştirici dostu temsilini döndürür, str yoksa print de bunu kullanır), len (len(nesne) için), getitem (nesne[anahtar] ile indeksleme için), add (+ operatörü için), eq (== operatörü için) vb. Bunlar daha ileri OOP konularıdır.
Bölüm 8: OOP Prensipleri ve Python
Şimdi OOP’nin temel prensiplerinin Python’da nasıl karşılık bulduğuna kısaca göz atalım.
8.1. Kapsülleme (Encapsulation)
Kapsülleme, veriyi (nitelikler) ve o veri üzerinde çalışan kodu (metotlar) tek bir birim (sınıf) içinde birleştirmek ve nesnenin iç durumunun dışarıdan doğrudan erişimini kısıtlamaktır (bilgi gizleme).
Python’da kapsülleme şu şekillerde sağlanır:
Veri ve Metotların Birleştirilmesi: Sınıflar doğal olarak nitelikleri ve metotları bir arada tutar.
Bilgi Gizleme (Convention): Python’da diğer dillerdeki gibi katı private veya protected erişim belirleyicileri yoktur. Bunun yerine konvansiyonlar kullanılır:
Tek Alt Çizgi () ile Başlayan İsimler (örn: self._gizli_veri): Geliştiriciler arasında "bu nitelik/metot sınıfın iç kullanımı içindir, dışarıdan doğrudan erişmeyin/kullanmayın" anlamına gelen bir işarettir. Python erişimi engellemez, sadece bir uyarıdır.
Çift Alt Çizgi () ile Başlayan İsimler (örn: self.cok_gizli): Bu durumda Python, isim karmaşasını (name mangling) uygular. Niteliğin/metodun adını arka planda _SinifAdicok_gizli şeklinde değiştirir. Bu, alt sınıfların yanlışlıkla aynı isimdeki bir niteliği/metodu ezmesini önlemek içindir, tam anlamıyla gizlilik sağlamaz (çünkü değiştirilmiş isme hala dışarıdan erişilebilir).
class Hesap:
def __init(self, sahip, bakiye=0):
self.sahip = sahip
self._bakiye = bakiye # 'Protected' konvansiyonu
self.islem_gecmisi = [] # 'Private' konvansiyonu (name mangling uygulanır)
def para_yatir(self, miktar):
if miktar > 0:
self._bakiye += miktar
self.islem_gecmisi.append(f"+{miktar}")
print(f"{miktar} TL yatırıldı. Yeni bakiye: {self._bakiye}")
else:
print("Geçersiz miktar.")
def para_cek(self, miktar):
if 0 < miktar <= self._bakiye:
self._bakiye -= miktar
self.islem_gecmisi.append(f"-{miktar}")
print(f"{miktar} TL çekildi. Yeni bakiye: {self._bakiye}")
else:
print("Yetersiz bakiye veya geçersiz miktar.")
def bakiye_goster(self):
print(f"Hesap Sahibi: {self.sahip}, Bakiye: {self._bakiye}")
def _gecmisi_goster(self): # 'Protected' metot konvansiyonu
print(f"İşlem Geçmişi: {self._islem_gecmisi}")
hesap1 = Hesap("Ali Can", 1000)
hesap1.para_yatir(500)
hesap1.para_cek(200)
hesap1.bakiye_goster()
Konvansiyona rağmen erişim MÜMKÜN (ama önerilmez)
print(f"Dışarıdan _bakiye erişimi: {hesap1._bakiye}")
hesap1._bakiye = 5000 # Doğrudan değiştirmek kapsüllemeyi bozar!
hesap1.bakiye_goster()
Name mangling nedeniyle doğrudan erişim ZOR (ama imkansız değil)
print(hesap1.__islem_gecmisi) # AttributeError verir!
print(f"Name mangling ile erişim: {hesap1.Hesap_islem_gecmisi}") # Çalışır ama asla yapılmamalı!
hesap1._gecmisi_goster() # Protected metoda erişim (önerilmez)
Kapsüllemenin amacı genellikle doğrudan erişimi kısıtlayıp, verilere kontrollü erişim sağlayan metotlar (getter/setter veya property’ler) sunmaktır, ancak Python’da bu daha çok konvansiyonlara dayanır.
8.2. Kalıtım (Inheritance)
Kalıtım, bir sınıfın (alt sınıf — subclass veya derived class) başka bir sınıfın (üst sınıf — superclass veya base class) niteliklerini ve metotlarını miras almasıdır. Bu, kod tekrarını önler ve sınıflar arasında hiyerarşik bir ilişki kurar (“is-a” ilişkisi).
Python’da kalıtım, sınıf tanımında üst sınıfın adını parantez içinde belirterek yapılır.
Sözdizimi: class AltSinif(UstSinif): ...
Üst Sınıf (Base Class)
class Hayvan:
def init(self, isim):
self.isim = isim
print("Hayvan nesnesi oluşturuldu.")
def yemek_ye(self):
print(f"{self.isim} yemek yiyor.")
def ses_cikar(self):
print("Hayvan sesi?")
Alt Sınıf (Derived Class) - Hayvan'dan miras alıyor
class Kedi(Hayvan): # Hayvan sınıfını miras al
def init(self, isim, renk):
# Üst sınıfın init'ini çağırmak ÖNEMLİDİR!
super().init(isim) # super() ile üst sınıfa referans alınır
self.renk = renk # Kedi'ye özel nitelik
print("Kedi nesnesi oluşturuldu.")
# Üst sınıftaki metodu GEÇERSİZ KILMA (Override)
def ses_cikar(self):
print(f"{self.isim} miyavlıyor!")
# Kedi'ye özel yeni bir metot
def miyavla(self):
print("Miyav!")
Başka bir Alt Sınıf
class Kopek(Hayvan):
def init(self, isim, cins):
super().init(isim)
self.cins = cins
print("Köpek nesnesi oluşturuldu.")
# ses_cikar metodunu override edelim
def ses_cikar(self):
print(f"{self.isim} havlıyor: Hav hav!")
Nesneleri oluşturalım
tekir = Kedi("Tekir", "Sarı")
karabas = Kopek("Karabaş", "Kangal")
print("\nMetot Çağrıları:")
tekir.yemek_ye() # Hayvan sınıfından miras alındı
karabas.yemek_ye() # Hayvan sınıfından miras alındı
tekir.ses_cikar() # Kedi sınıfında override edildi
karabas.ses_cikar() # Köpek sınıfında override edildi
tekir.miyavla() # Kedi sınıfına özel metot
karabas.miyavla() # Hata! Köpek sınıfında bu metot yok
super() Fonksiyonu: Alt sınıflarda, özellikle init içinde, üst sınıfın metotlarını çağırmak için super() kullanılır. super().init(...), üst sınıfın başlatıcısını çağırarak miras alınan niteliklerin düzgün bir şekilde ayarlanmasını sağlar.
8.3. Çok Biçimlilik (Polymorphism)
Çok biçimlilik, farklı sınıflara ait nesnelerin aynı arayüze (örneğin aynı metot adına) farklı şekillerde yanıt verebilmesi yeteneğidir. Kelime anlamı “çok biçimli”dir.
Python’da çok biçimlilik genellikle iki şekilde kendini gösterir:
Metot Geçersiz Kılma (Method Overriding): Yukarıdaki kalıtım örneğinde gördüğümüz gibi, alt sınıflar üst sınıftan miras aldıkları metotları kendi ihtiyaçlarına göre yeniden tanımlayabilirler (override). tekir.ses_cikar() "Miyav!" derken, karabas.ses_cikar() "Hav hav!" der. Aynı metot adı, farklı nesnelerde farklı davranışlar sergiler.
Ördek Testi (Duck Typing): Python’un dinamik tipli doğası nedeniyle ortaya çıkan bir polimorfizm şeklidir. Python, bir nesnenin belirli bir metoda sahip olup olmadığına bakar, hangi sınıftan geldiğine değil. “Eğer bir kuş gibi yürüyorsa, yüzüyorsa ve vaklıyorsa, o zaman ona ördek derim.” prensibine dayanır. Bir fonksiyon, farklı türlerdeki nesneleri kabul edebilir, yeter ki bu nesneler fonksiyonun ihtiyaç duyduğu metotlara veya niteliklere sahip olsun.
def hayvan_konustur(hayvan_nesnesi): # Nesnenin tipine bakmadan doğrudan ses_cikar metodunu çağırıyoruz hayvan_nesnesi.ses_cikar() # Önceki Kedi ve Köpek nesnelerimiz tekir = Kedi("Tekir", "Sarı") karabas = Kopek("Karabaş", "Kangal") # Farklı türdeki nesneleri aynı fonksiyona gönderebiliriz print("\nPolimorfizm (Duck Typing):") hayvan_konustur(tekir) # Tekir miyavlıyor! hayvan_konustur(karabas) # Karabaş havlıyor: Hav hav! # Eğer ses_cikar metodu olmayan bir nesne gönderirsek hata alırız: class Kus: def init(self, isim): self.isim = isim def uc(self): print(f"{self.isim} uçuyor.") marti = Kus("Martı") # hayvan_konustur(marti) # AttributeError: 'Kus' object has no attribute 'ses_cikar'
Polimorfizm, kodun daha esnek ve genişletilebilir olmasını sağlar. Yeni hayvan türleri eklediğimizde, eğer ses_cikar metoduna sahiplerse, hayvan_konustur fonksiyonunu değiştirmemize gerek kalmaz.
Bölüm 9: Daha Kapsamlı Bir Örnek
Şimdi öğrendiklerimizi birleştirerek biraz daha kapsamlı bir örnek yapalım:
import math
class Sekil:
"""Geometrik şekiller için temel sınıf."""
def init(self, renk="Siyah"):
self.renk = renk # Protected nitelik
print("Şekil oluşturuldu.")
def alan(self):
# Üst sınıfta genel bir metot (veya soyut bırakılabilir)
raise NotImplementedError("Alt sınıflar alan() metodunu implemente etmeli!")
def cevre(self):
raise NotImplementedError("Alt sınıflar cevre() metodunu implemente etmeli!")
def renk_getir(self):
return self._renk
def __str(self):
return f"Bir şekil (Renk: {self._renk})"
class Daire(Sekil):
"""Daire şeklini temsil eden sınıf."""
pi = math.pi # Sınıf niteliği
def __init(self, yaricap, renk="Mavi"):
super().init(renk) # Üst sınıfın __init'ini çağır
if yaricap < 0:
raise ValueError("Yarıçap negatif olamaz.")
self.yaricap = yaricap
print("Daire oluşturuldu.")
def alan(self):
# Üst sınıf metodunu override et
return self.pi * (self.yaricap ** 2)
def cevre(self):
return 2 * self.pi * self.yaricap
def cap_getir(self):
return 2 * self.yaricap
def __str(self):
# Üst sınıf __str'ünü de çağırıp üzerine ekleyebiliriz
# temel_str = super().str()
return f"Daire (Yarıçap: {self.yaricap}, Renk: {self.renk_getir()})"
class Dikdortgen(Sekil):
"""Dikdörtgen şeklini temsil eden sınıf."""
def __init(self, genislik, yukseklik, renk="Kırmızı"):
super().init(renk)
if genislik < 0 or yukseklik < 0:
raise ValueError("Genişlik ve yükseklik negatif olamaz.")
self.genislik = genislik
self.yukseklik = yukseklik
print("Dikdörtgen oluşturuldu.")
def alan(self):
return self.genislik * self.yukseklik
def cevre(self):
return 2 * (self.genislik + self.yukseklik)
def __str_(self):
return f"Dikdörtgen (Genişlik: {self.genislik}, Yükseklik: {self.yukseklik}, Renk: {self.renk_getir()})"
Nesneleri oluşturalım
try:
daire1 = Daire(5)
dikdortgen1 = Dikdortgen(10, 4, renk="Yeşil")
sekil1 = Sekil() # Üst sınıf nesnesi
# daire_hatali = Daire(-2) # ValueError fırlatır
sekiller = [daire1, dikdortgen1, sekil1]
print("\n--- Şekil Bilgileri ---")
for sekil in sekiller:
print(sekil) # Her nesne kendi str metodunu kullanır (Polimorfizm)
try:
# alan() metodunu çağırmayı dene
print(f" Alan: {sekil.alan():.2f}") # Polimorfizm
except NotImplementedError:
print(" Alan hesaplanamadı (implemente edilmemiş).")
except Exception as e:
print(f" Alan hesaplama hatası: {e}")
print("-" * 20)
except ValueError as e:
print(f"\nNesne oluşturma hatası: {e}")
Bu örnekte kalıtım, metot geçersiz kılma, super()
, __init__
, __str__
ve temel polimorfizm konseptleri bir arada kullanılmıştır.
Sonuç
Python’da sınıflar ve nesneler, Nesne Yönelimli Programlamanın temelini oluşturur. Sınıflar, nesnelerin yapısını ve davranışlarını tanımlayan şablonlar iken, nesneler bu şablonlardan oluşturulan, kendi durumlarına (nitelikler) ve davranışlarına (metotlar) sahip somut örneklerdir.
class anahtar kelimesi ile sınıf tanımlarız. init metodu, nesneler oluşturulduğunda başlangıç durumlarını ayarlamak için kullanılır. self parametresi, metotların çalıştığı nesnenin kendisine erişmesini sağlar. Sınıf nitelikleri tüm örnekler tarafından paylaşılırken, örnek nitelikleri her nesneye özeldir. Özel metotlar (init, str gibi) Python'un yerleşik mekanizmalarıyla entegrasyon sağlar.
OOP’nin temel prensipleri olan kapsülleme (veri ve metotları birleştirme, bilgi gizleme konvansiyonları), kalıtım (kod tekrarını azaltma, hiyerarşi kurma) ve çok biçimlilik (farklı nesnelerin aynı arayüze farklı yanıtlar vermesi, esneklik) Python’da güçlü bir şekilde desteklenir.
OOP yaklaşımı, özellikle orta ve büyük ölçekli projelerde kodun daha organize, modüler, anlaşılır, yeniden kullanılabilir ve bakımı kolay olmasını sağlar. Bu temel kavramları anlamak, Python’da daha gelişmiş ve yapılandırılmış programlar yazmanın kapısını aralar.
Abdulkadir Güngör - Kişisel WebSite
Abdulkadir Güngör - Kişisel WebSite
Abdulkadir Güngör - Özgeçmiş
Github
Github
Linkedin