Kalıtım ve metot ezme

Kalıtım (inheritance), bir sınıfın başka bir sınıfın davranışlarını ve arayüzünü temel alarak özelleşmesini sağlar. Güçlü bir araçtır; ancak yanlış kullanıldığında sıkı bağımlı ve kırılgan sınıf hiyerarşileri oluşturabilir. Bu hafta kalıtımı is-a ilişkisi üzerinden öğrenecek ve kompozisyon alternatifiyle karşılaştıracağız.

Bu bölümün kapsamı

NoteSınavda sorulur
  • alt sınıfın üst sınıftan davranış devralması ve tür ilişkisi
  • kalıtım kararını yerine geçme sorusuyla sınama
  • bir metodu override ederek davranışı özelleştirme
  • super() çağrısının normal metotta da kullanılması
  • is-a ile has-a ayrımı; ortak alanın kalıtım gerekçesi olmaması
  • derin kalıtım zincirinin kırılganlığı
  • çoklu kalıtım ve MRO terimlerinin tanınması
  • çoklu kalıtım tasarımı ve mixin kalıpları
  • MRO algoritmasının (C3) ayrıntısı
  • soyut temel sınıf (ABC) yazımı

Bu başlıklar konunun devamıdır; ileride karşınıza çıkar ama bu derste ezberlemeniz beklenmiyor.

Bu iki sınıf arasındaki ilişki is-a mı, has-a mı?

Kalıtım güçlü ama sıkı bir bağdır. “Bir X, bir Y’dir” cümlesini kurabilmek tek başına yeterli değildir. Cümle rahatça kurulduğu hâlde kalıtımın yanlış kaldığı durumları da göreceğiz.

İlk kalıtım örneği

Developer, Employee sınıfından davranış alır. Bir Developer nesnesi aynı zamanda Employee tür ilişkisinin de içindedir; bu nedenle iki isinstance() çağrısı da True olur.

is-a yeterli mi? Liskov yerine geçme ilkesi

Kalıtım için ilk sezgi:

Bir Developer, bir Employee’dir.

Ama daha güçlü soru şudur:

Bir Employee beklenen yerde Developer kullanıldığında üst sınıfın beklenen sözleşmesi bozulmadan program çalışıyor mu?

Bu düşünce yazılım tasarımında Liskov Substitution Principle (LSP) / Liskov yerine geçme ilkesi ile ilişkilidir. Bu derste ilkenin formal tanımını ezberlemiyoruz; alt türün üst türün beklenen davranışını bozup bozmadığını sorguluyoruz.

Override: davranışı özelleştirmek

Alt sınıftaki send, üst sınıftaki aynı adlı metodu ezer (override). Override ederken çağıranın beklediği davranış sözleşmesini korumaya çalışın.

Alt sınıfın üst sınıftan davranış devralması ve bir metodu override ederek özelleştirmesini gösteren diyagram.

Kalıtım ve override

super() yalnızca __init__ için değildir

Önce kurucuda yeniden kullanım:

super().__init__(name), üst sınıf kurulumunu tekrar yazmamayı sağlar.

Alt sınıf kurucusundan super().__init__ çağrısıyla üst sınıf kurucusuna geçişi gösteren akış.

super() çağrı akışı

Asıl davranışı genişletme örneği normal metotta da görülebilir:

Burada alt sınıf üst davranışı tamamen silip yeniden yazmak yerine çağırır ve sonucu genişletir.

is-a ve has-a

İlişki Örnek Tipik araç
is-a Developer bir Employee’dir kalıtım
has-a Car bir Engine’e sahiptir kompozisyon

Ortak alan veya metot bulunması tek başına kalıtım gerekçesi değildir. Driver ve Car ikisinde de name bulunabilir; ama Driver is a Car anlamsızdır.

Kırılgan hiyerarşi ve MRO kavram radarı

Derin kalıtım zincirleri üst sınıftaki küçük değişikliklerin çok sayıda alt sınıfa yayılmasına yol açabilir. Bu derste iki-üç seviyeden daha derin hiyerarşiler tasarlamayacağız.

Python ayrıca çoklu kalıtıma izin verir:

class C(A, B):
    pass

Bu durumda aynı adlı davranışın hangi sırayla aranacağını method resolution order (MRO) belirler. C.mro() ile bu sıra görülebilir. Çoklu kalıtım ve MRO bu dersin uygulama/ölçme kapsamı değildir; yalnızca internette karşılaştığınızda terimi tanımanız beklenir.

WarningKalıtım öğrendik diye her yerde kullanmayın

İyi OOP tasarımı mümkün olduğunca çok hiyerarşi üretmek değildir. Kompozisyon çoğu durumda daha gevşek ve değiştirilebilir bir bağ kurar.

Alıştırma — Uygun ilişkiyi seç

Dog–Animal, Order–Customer, Laptop–Battery, Teacher–Course, AdminUser–User çiftlerinde is-a, has-a veya “ikisi de değil” seçin. is-a seçtiğiniz her durumda ayrıca “üst tür beklenen yerde alt tür güvenle kullanılabilir mi?” sorusunu yanıtlayın.

Alıştırma — Override tamamla

return self.width * self.height
Note

NotImplementedError burada “alt sınıf bu davranışı sağlamalı” beklentisini görünür kılan öğretim örneğidir; sınıfı teknik olarak soyut yapmaz. 12. haftada NotImplemented ile NotImplementedError arasındaki kritik farkı ayrıca göreceğiz.

Alıştırma — Normal metotta super() kullan

Bir Logger.log(message) metodu "LOG: ..." döndürsün. TimestampLogger, log() metodunu override etsin; önce super().log(message) çağırıp sonra sonuca " [zaman damgalı]" eklesin. Üst metodu kopyalamadan davranışı genişletin.

Küçük üretim görevi — Ödeme türleri

Bir Payment üst sınıfı ile CardPayment ve CashPayment alt sınıflarını tasarlayın. Her ikisi pay(amount) sunsun. En az bir alt sınıfta super() ile normal bir üst metodu genişletin.

Çalışır kod teslimi: İki alt türü aynı listede tutup pay() çağrılarını çalıştırın; ayrıca isinstance(child, Parent) sonucunu assert ile doğrulayın. Bir geçersiz kalıtım örneği yazıp neden LSP açısından sorunlu olduğunu iki cümleyle açıklayın.

Sınav provası

Önce kendi cevabını seç; sonra cevap anahtarında her şıkkın neden doğru veya yanlış olduğunu oku. Çeldiriciler uydurma değil, bu konuda gerçekten yapılan hatalardır. Sınav maddeleri de bu mantıkla yazılır.

Madde 1. Aşağıdaki program ne yazdırır?

class Employee:
    def __init__(self, name):
        self.name = name

    def describe(self):
        return f"Çalışan: {self.name}"

class Developer(Employee):
    pass

dev = Developer("Ada")
print(dev.describe())
print(isinstance(dev, Employee))
  • A. Çalışan: Ada ve True
  • B. Geliştirici: Ada ve True
  • C. TypeError; pass gövdeli bir sınıf örneklenemez
  • D. AttributeError ve True
  • E. Çalışan: Ada ve False

Madde 2. Kalıtım kararı verirken is-a sezgisinden daha güçlü olan soru nedir?

  • A. Üst sınıf __init__ tanımlıyor mu
  • B. Üst sınıfın beklendiği bir yerde alt sınıf kullanıldığında sözleşme bozulmadan program çalışıyor mu
  • C. Alt sınıf üst sınıfla aynı dosyada mı
  • D. Alt sınıf üst sınıftan daha az metot mu içeriyor
  • E. İki sınıfta ortak nitelik var mı

Madde 3. Bir Square sınıfını Rectangle sınıfından türetmek neden sorun çıkarabilir?

  • A. Sorun yoktur; matematiksel olarak kare bir dikdörtgendir
  • B. Square sınıfı Rectangle sınıfından daha az nitelik taşıdığı için AttributeError oluşur
  • C. Rectangle beklenen yerde genişliği ve yüksekliği bağımsız ayarlanabilir bir nesne varsayılır; Square bu sözleşmeyi koruyamaz
  • D. Python’da geometrik sınıflar türetilemez
  • E. Kalıtım yalnızca aynı sayıda parametre alan kurucularda geçerlidir

Madde 4. Aşağıdaki program ne yazdırır?

class Notification:
    def send(self, message):
        return f"Bildirim: {message}"

class EmailNotification(Notification):
    def send(self, message):
        return f"E-posta: {message}"

print(EmailNotification().send("Merhaba"))
  • A. Bildirim: Merhaba
  • B. TypeError; aynı adlı iki metot tanımlanamaz
  • C. Önce Bildirim: Merhaba, sonra E-posta: Merhaba
  • D. E-posta: Merhaba
  • E. None

Madde 5. Aşağıdaki program ne yazdırır?

class Notification:
    def send(self, message):
        return f"Gönderildi: {message}"

class EmailNotification(Notification):
    def send(self, message):
        base_result = super().send(message)
        return f"{base_result} [kanal=e-posta]"

print(EmailNotification().send("Toplantı 14.00"))
  • A. Gönderildi: Toplantı 14.00
  • B. RecursionError; super().send kendisini çağırır
  • C. TypeError; super() yalnızca __init__ içinde kullanılabilir
  • D. Toplantı 14.00 [kanal=e-posta]
  • E. Gönderildi: Toplantı 14.00 [kanal=e-posta]

Madde 6. Alt sınıf kurucusunda super().__init__(name) yazmanın gerekçesi nedir?

  • A. Üst sınıfın başlangıç kurulumunu tekrar yazmadan çalıştırmak; kural değişince tek yer güncellenir
  • B. Üst sınıfın örneğini ayrıca üretmek
  • C. Python’un zorunlu kıldığı bir kuralı yerine getirmek
  • D. Alt sınıfın üst sınıf metotlarını override etmesini sağlamak
  • E. Alt sınıfın kendi niteliklerini otomatik oluşturmak

Madde 7. Driver ve Car sınıflarının ikisinde de name niteliği bulunması ne anlatır?

  • A. İki sınıfın tek sınıfta birleştirilmesi gerektiğini
  • B. Hiçbir kalıtım gerekçesi anlatmaz; ortak alan bulunması is-a ilişkisi kurmaz
  • C. Driver sınıfının Car sınıfından türetilebileceğini
  • D. İkisinin ortak bir üst sınıftan türetilmesi gerektiğini
  • E. Car sınıfının bir Driver niteliği taşıması gerektiğini

Madde 8. Derin kalıtım zincirleri neden kırılgandır?

  • A. Derin zincirler bellek kullanımını artırır
  • B. super() üçten fazla seviyede çalışmaz
  • C. Üst sınıftaki küçük bir değişiklik çok sayıda alt sınıfa yayılır ve etkisi izlenemez hâle gelir
  • D. Alt sınıflar üst sınıf metotlarını çağıramaz hâle gelir
  • E. Python üç seviyeden derin kalıtıma izin vermez

Madde 9. “Kalıtım öğrendik diye her yerde kullanmayın” uyarısının gerekçesi nedir?

  • A. Kalıtım Python’da yavaş çalıştığı için
  • B. Kalıtım kullanılan sınıflar test edilemez
  • C. Python’da çoklu kalıtım bulunmadığı için
  • D. Kompozisyon çoğu durumda daha gevşek ve değiştirilebilir bir bağ kurar
  • E. Kalıtım yalnızca standart kütüphane sınıflarında kullanılmalıdır

Madde 10. class C(A, B): yazıldığında MRO (method resolution order) neyi belirler?

  • A. Sınıfların bellekte saklanma sırasını
  • B. Hangi sınıfın önce örnekleneceğini
  • C. __init__ metotlarının kaç kez çalışacağını
  • D. Hangi sınıfın niteliklerinin kopyalanacağını
  • E. Aynı adlı bir davranışın hangi sırayla aranacağını

Madde 1 — Doğru: A. Developer gövdesi boş olsa da __init__ ve describe üst sınıftan devralınır. Bir Developer aynı zamanda Employee tür ilişkisindedir.

  • B yanlış: Developer metodu override etmemiştir; üst sınıfın metni kullanılır.
  • C yanlış: Boş gövdeli sınıf geçerlidir ve örneklenebilir.
  • D yanlış: describe üst sınıfta tanımlıdır ve bulunur.
  • E yanlış: isinstance() üst sınıfları da dikkate alır.

Madde 2 — Doğru: B. Yerine geçme sorusu (Liskov ilkesi) alt türün üst türün beklenen davranışını koruyup korumadığını sınar.

  • A yanlış: Kurucunun varlığı yerine geçme sorusunu cevaplamaz.
  • C yanlış: Dosya düzeni tasarım kararı değildir.
  • D yanlış: Metot sayısının tür ilişkisiyle bağı yoktur.
  • E yanlış: Ortak alan bulunması tek başına kalıtım gerekçesi değildir.

Madde 3 — Doğru: C. Kenarları bağımsız değiştirilebilen bir dikdörtgen bekleyen kod, kareyle çalışırken şaşırtıcı sonuç alır. is-a cümlesi kurulabilse de yerine geçme bozulur.

  • A yanlış: Matematiksel doğruluk davranış sözleşmesini garanti etmez; sorulan da budur.
  • B yanlış: Devralınan nitelikler kaybolmaz.
  • D yanlış: Böyle bir kısıt yoktur; sorun tasarımdadır.
  • E yanlış: Alt sınıf farklı imzalı kurucu tanımlayabilir.

Madde 4 — Doğru: D. Alt sınıftaki send üst sınıftaki aynı adlı metodu ezer; arama önce örneğin sınıfında yapılır.

  • A yanlış: Üst sınıf metodu ancak alt sınıfta aynı ad tanımlı değilse çalışır.
  • B yanlış: Farklı sınıflarda aynı ad kullanılabilir; override budur.
  • C yanlış: Override ile üst metot kendiliğinden çağrılmaz.
  • E yanlış: Metot bir metin döndürür.

Madde 5 — Doğru: E. super().send(message) üst sınıf davranışını çalıştırır; alt sınıf sonucu silmek yerine genişletir.

  • A yanlış: Alt sınıf sonuca kendi ekini de katar.
  • B yanlış: super() bir üstteki sınıfın metodunu çağırır; özyineleme oluşmaz.
  • C yanlış: Her metotta kullanılabilir; asıl davranış genişletme örneği budur.
  • D yanlış: Üst sınıfın metni de sonuca girer.

Madde 6 — Doğru: A. Üst sınıfın __init__ içindeki doğrulama ve atamalar kopyalanmadan kullanılır.

  • B yanlış: Kalıtımda ikinci bir nesne oluşmaz; aynı örnek başlatılır.
  • C yanlış: Zorunlu değildir; yazılmazsa üst kurulum hiç çalışmaz.
  • D yanlış: Override için super() gerekmez.
  • E yanlış: Alt sınıfın nitelikleri ayrıca yazılır.

Madde 7 — Doğru: B. Driver is a Car cümlesi anlamsızdır. Ortak nitelik yalnız bir rastlantıdır.

  • A yanlış: Sorumlulukları ayrıdır.
  • C yanlış: Sürücü bir araba değildir.
  • D yanlış: Ortak ad paylaşımı soyut bir üst tür oluşturmaz.
  • E yanlış: Gerekebilir ama bunu name alanının ortaklığı söylemez.

Madde 8 — Doğru: C. Bağ sıkıdır; bu yüzden bu derste iki-üç seviyeden derin hiyerarşi tasarlanmaz ve kompozisyon tercih edilir.

  • A yanlış: Sorun bakım maliyetidir.
  • B yanlış: Zincir boyunca çalışır.
  • D yanlış: Çağırabilirler; zaten sorun bu bağın sıkılığıdır.
  • E yanlış: Teknik bir sınır yoktur; sınır tasarım kararıdır.

Madde 9 — Doğru: D. İyi tasarım mümkün olduğunca çok hiyerarşi üretmek değildir; parçaları değiştirilebilir tutmaktır.

  • A yanlış: Gerekçe başarım değildir.
  • B yanlış: Test edilebilir; sorun bağın sıkılığıdır.
  • C yanlış: Çoklu kalıtım vardır; kapsam dışı bırakılmıştır.
  • E yanlış: Kendi sınıflarımızda da yerinde kullanılabilir.

Madde 10 — Doğru: E. C.mro() bu sırayı gösterir. Çoklu kalıtım bu dersin uygulama ve ölçme kapsamı değildir; terimi tanımak yeterlidir.

  • A yanlış: Bellek yerleşimiyle ilgisi yoktur.
  • B yanlış: Tek bir örnek üretilir; sıralama arama düzenidir.
  • C yanlış: Çağrı sayısı yazılan super() kullanımına bağlıdır.
  • D yanlış: Nitelikler kopyalanmaz; aranır.

Tek sayfa özet

  • Kalıtım bir is-a ilişkisidir; alt tür üst türün beklenen sözleşmesini korumalıdır.
  • Bu davranışsal beklenti Liskov yerine geçme ilkesiyle ilişkilidir.
  • Alt sınıf davranışı miras alabilir, ekleyebilir veya override edebilir.
  • super() yalnızca kurucuda değil, normal davranışı genişletirken de kullanılabilir.
  • Alt sınıf örneği üst sınıf açısından da isinstance olabilir.
  • Çoklu kalıtımda MRO önemlidir; bu derste tanıma düzeyindedir.
  • Kompozisyon her zaman gerçek bir alternatiftir.

Bu bölümün kazanımları

Bu bölümü bitiren öğrenci:

  • Alt sınıfın üst sınıftan davranış devraldığını ve tür ilişkisine girdiğini gösterir.
  • Kalıtım kararını is-a sezgisiyle değil, yerine geçme sorusuyla sınar.
  • Bir metodu override ederek davranışı özelleştirir.
  • super() çağrısını yalnız kurucuda değil normal metotta da davranışı genişletmek için kullanır.
  • is-a ile has-a ilişkisini ayırır ve ortak alanı kalıtım gerekçesi saymaz.
  • Derin kalıtım zincirinin kırılganlığını açıklar.
  • Çoklu kalıtım ve MRO terimlerini tanıma düzeyinde açıklar.
Back to top