Kalıtım ve metot ezme

Kalıtım (inheritance), bir sınıfın başka bir sınıfın niteliklerini ve metotlarını yeniden yazmadan devralmasıdır. Devralan sınıfa alt sınıf, devredilen sınıfa üst sınıf denir. Alt sınıf, devraldığı metotlardan gerekenleri kendine göre değiştirebilir. Kalıtım yanlış yerde kullanılırsa sınıflar birbirine sıkı bağlanır ve üst sınıftaki küçük bir değişiklik alt sınıfları bozabilir. Örneklerin çoğu bir şirketin personel programından. Program her çalışanın adını tutuyor ve çalışanı tek satırla tanıtıyor: Çalışan: Ada. Şirkette yazılımcılar ve yöneticiler de var. Bu hafta kalıtımı geçen hafta gördüğünüz kompozisyonla da karşılaştıracağız.

Bu sayfadaki kod hücreleri kendiliğinden çalışmaz. Her hücrede önce çıktıyı tahmin edin, sonra Run Code düğmesine basın. Kodu değiştirip yeniden çalıştırabilir, Start Over ile ilk hâline döndürebilirsiniz.

Bu bölümün kapsamı

NotSınavda sorulur
  • Alt sınıfın üst sınıftan davranış devralması ve tür ilişkisi
  • Bir metodu override ederek davranışı özelleştirme
  • super() çağrısının normal metotta da kullanılması
  • Kalıtım kararını yerine geçme sorusuyla sınama
  • is-a ile has-a ayrımı, ortak alanın kalıtım gerekçesi olmaması
  • Derin kalıtım zincirinin kırılganlığı
  • Override edilen ve devralınan metotların çağrıda hangi sınıftan geldiğini izleme
  • Çoklu kalıtım ve MRO terimleri
  • Ç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. İleride karşınıza çıkar, ama bu derste ezberlemeniz beklenmiyor.

Alt sınıf, üst sınıfın metotlarını devralır

Yazılımcının da adı var ve o da kendini tanıtmalı. Çalışan için yazılmış kodu Developer sınıfına kopyalamak istemiyoruz. Python’da class Developer(Employee): yazdığınızda Developer sınıfını Employee sınıfından türetirsiniz: parantez içindeki Employee üst sınıf, Developer alt sınıf olur. Hücredeki isinstance(nesne, Sınıf) fonksiyonu, nesnenin o sınıftan sayılıp sayılmadığını sorar ve True ya da False döndürür.

Developer sınıfının gövdesinde yalnız pass var. dev.describe() çalışır mı? İki isinstance() ne yazar?

Developer sınıfında ne __init__ var ne describe(). Python bir metodu önce nesnenin kendi sınıfında arar, orada bulamazsa üst sınıfa bakar. Alt sınıfın nesnesi üst sınıfın da örneği sayılır: her Developer aynı zamanda bir Employee’dir. Bu ilişkiye İngilizcede is-a (“bir … türüdür”) denir.

Override: davranışı özelleştirmek

Yönetici başka bir unvanla tanıtılmalı. Aşağıdaki Manager alt sınıfı, üst sınıftaki describe() ile aynı adda bir metot yazıyor. Manager sınıfında __init__ yok. İki satır ne yazar? İkinci satırdaki ad nereden geliyor?

Alt sınıftaki describe(), üst sınıftaki aynı adlı metodu ezer (override): bir Manager nesnesinde describe() çağrılınca alt sınıftaki sürüm çalışır. Manager sınıfında yazılmayan __init__ ise yine üst sınıftan gelir ve name niteliğini kurar. Override ederken metodu çağıran kodun beklentisini bozmayın. Burada iki sürüm de parametre almıyor ve bir metin döndürüyor.

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() ile üst sınıfın metodunu çağırmak

Yazılımcı programlama dilini de tutmalı. Bunun için Developer kendi __init__ metodunu yazmalı. Ama self.name = name satırını alt sınıfta yeniden yazmak, çalışanın kurulum kodunu kopyalamak olur. Alt sınıfın içinden üst sınıfın bir metodunu super() ile çağırırsınız.

Hücredeki Developer sınıfında self.name = name satırı yok. İki print() ne yazar?

super().__init__(name) üst sınıfın __init__ metodunu aynı nesne için çalıştırır ve name niteliğini kurar. language niteliğini ise alt sınıf kendisi kurar. Kendi __init__ metodunu yazan alt sınıf super().__init__() çağrısını unutursa üst sınıfın kurulumu hiç çalışmaz ve self.name oluşmaz.

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ışı

super() normal metotlarda da çalışır. Yazılımcının tanıtımı, çalışanın tanıtımının sonuna dili eklesin. Üst sınıfın sonucunu alıp üstüne bir şey eklemeye davranışı genişletmek denir. Son satır ne yazar?

Alt sınıf, üst sınıfın describe() metodunu silip baştan yazmaz. Onu çağırır ve dönen metnin sonuna dili ekler. Çalışanın tanıtım biçimi değişirse yazılımcının tanıtımı da kendiliğinden değişir.

Yerine geçme: Liskov ilkesi

Şimdi bütün personeli tek fonksiyonla tanıtalım. introduce_all() fonksiyonu çalışanlar için yazıldı ve listedeki her kişinin describe() metodunu çağırıyor. Listede bir çalışan, bir yönetici ve bir yazılımcı var. Fonksiyon ne yazar?

Fonksiyon kimin hangi sınıftan olduğunu sormadı. Her nesnede describe() çağrıldı ve her nesne kendi sınıfındaki sürümü çalıştırdı. Bu güvenle yapılabiliyor, çünkü üç sınıfta da describe() parametre almıyor ve bir metin döndürüyor.

  1. haftada bir fonksiyonun sözleşmesini, yani ne alıp ne döndürdüğünü ve neyi değiştirdiğini anlatan tanımı görmüştünüz. Bir sınıfın metotlarının sözleşmesi de o sınıfı kullanan kodun güvendiği sözdür. “Bir Developer, bir Employee’dir” cümlesi kalıtım kararını tek başına vermez. Şunu da sorun:

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

Yazılım tasarımında bu soru Liskov yerine geçme ilkesi (Liskov Substitution Principle, LSP) adıyla bilinir. İlkenin biçimsel tanımını ezberlemeyeceğiz. Alt sınıfın, üst sınıftan beklenen davranışı bozup bozmadığına bakacağız.

Bu soru bazen “bir … türüdür” cümlesinden farklı bir cevap verir. Kare matematikte bir dikdörtgendir. Aşağıda Square sınıfı Rectangle sınıfından türetiliyor ve kenarları eşit kalsın diye set_width() metodunu override ediyor. widen() fonksiyonu dikdörtgenler için yazıldı: genişliği 10 yapıp alanı döndürüyor. İki nesnenin de yüksekliği 5. İki satır ne yazar?

widen(), genişliği değiştirince yüksekliğin aynı kalacağına güveniyor. Rectangle sınıfının sözü de bu: set_width() yalnız genişliği değiştirir. Square kendi kuralını, yani kenarların eşit kalmasını korurken bu sözü bozuyor. “Kare bir dikdörtgendir” cümlesi doğru olsa da Square bu programda Rectangle sınıfının yerine geçemiyor.

İlk satır 50 yazar: dikdörtgenin genişliği 10, yüksekliği 5 olur. İkinci satır 100 yazar: Square.set_width() yüksekliği de 10 yapar. Yüksekliği 5 olan bir dikdörtgen bekleyen widen() için bu şaşırtıcı bir sonuçtur. Square sınıfı set_width() metodunu override etmeseydi bu kez kare 10’a 5 olur, kare olmaktan çıkardı.

is-a ve has-a

Şirket arabalarını da kaydetmek istiyoruz. Her arabayı bir çalışan kullanıyor ve arabanın da çalışan gibi bir adı var. name niteliğini yeniden yazmamak için Car sınıfını Employee sınıfından türetmek akla gelebilir. Ama “araba bir çalışandır” cümlesi anlamsız. Araba bir çalışana sahiptir: onu kullanan sürücüye. Geçen hafta bu ilişkinin adının has-a olduğunu ve kompozisyonla kurulduğunu gördünüz.

Hücredeki Car sınıfı Employee sınıfından türemiyor, sürücüyü bir nitelikte tutuyor. Üç satır ne yazar?

Car sınıfı tanıtma işini sürücüye bırakır, ama kendisi bir çalışan sayılmaz.

İlişki Cümle Örnek Araç
is-a “X bir Y türüdür” Developer bir Employee’dir kalıtım
has-a “X’in bir Y’si vardır” Car bir sürücüye (Employee) sahiptir kompozisyon

İki sınıfta aynı adlı bir nitelik ya da metot bulunması, birini ötekinden türetmek için gerekçe olmaz. Car ve Employee sınıflarının ikisinde de name var, ama araba bir çalışan değildir.

Tanımanız yeterli: kırılgan hiyerarşi ve MRO

Üst ve alt sınıfların oluşturduğu ağaca sınıf hiyerarşisi denir. Kalıtım zinciri derinleştikçe, yani B sınıfı A’dan, C sınıfı B’den, D sınıfı C’den türetildikçe, A sınıfındaki küçük bir değişiklik bütün alt sınıflara yayılır. Bu derste iki üç seviyeden derin hiyerarşi tasarlamayacağız.

Python bir sınıfın birden fazla üst sınıftan türetilmesine de izin verir. Buna çoklu kalıtım denir:

class C(A, B):
    pass

A ve B sınıflarında aynı adlı bir metot varsa Python önce hangisine bakar? Bu arama sırasına method resolution order (MRO) denir. Sırayı C.mro() ile görebilirsiniz. Çoklu kalıtım ve MRO alıştırmalarda ve sınavda sorulmaz. İnternette bu terimlerle karşılaştığınızda ne olduklarını bilmeniz yeter.

UyarıKalıtım öğrendik diye her yerde kullanmayın

Bir tasarımda üst ve alt sınıf sayısı arttıkça tasarım iyileşmez. Kompozisyon çoğu durumda daha gevşek ve kolay değiştirilen bir bağ kurar. Car sürücüsünü bir nitelikte tutar. Sürücüyü değiştirmek için car.driver niteliğine başka bir çalışan atamanız yeter. Car sınıfına dokunmanız gerekmez.

C ile Python yan yana

Personel programındaki işlerin C’deki ve Python’daki karşılıkları:

İş C’de Bu haftaki Python yolu Dikkat
Yazılımcıyı çalışanın kodunu kopyalamadan tanıtmak struct Developer içine Employee alanlarını kopyalarsınız ya da bir struct Employee alanı koyarsınız class Developer(Employee): Alt sınıfın nesnesi üst sınıfın da örneğidir: isinstance(dev, Employee) True döndürür
Yöneticiyi başka bir unvanla tanıtmak struct’a bir tür alanı ekler, switch ile doğru printf() satırını seçersiniz Manager içinde aynı adlı describe() metodu (override) Override, üst sınıftaki metodu kendiliğinden çağırmaz
Yazılımcıyı diliyle tanıtmak Çalışanı yazdıran fonksiyonu çağırır, ardından dili yazdırırsınız super().__init__(name) ve super().describe() Kendi __init__ metodunu yazan alt sınıf super().__init__() çağırmazsa self.name oluşmaz
Bütün personeli tek fonksiyonla tanıtmak Döngüde tür alanına göre switch yazarsınız Döngüde person.describe() Alt sınıf üst sınıfın sözünü bozarsa (kare ve dikdörtgen) fonksiyon yanlış sonuç alır
Şirket arabasını sürücüsüyle kaydetmek struct Car içine sürücü için bir struct Employee * alanı koyarsınız self.driver = driver (kompozisyon) Ortak name niteliği, Car sınıfını Employee sınıfından türetmek için gerekçe olmaz

C’de Employee alanlarını yeni bir struct’a kopyalarsanız, çalışana yeni bir alan eklendiğinde iki yeri birden değiştirmeniz gerekir. Kalıtımda alan ve metot tek yerde durur. Yöneticiyi ayırmak ve bütün personeli tanıtmak için C’de tür alanı ve switch yazardınız. Python’da bu dallanmayı hiç yazmadık: hangi describe() metodunun çalışacağını nesnenin sınıfı seçti. Gelecek hafta bu fikre polimorfizm adıyla döneceğiz. Arabaya sürücüyü gösteren bir alan koymak ise C’de de kompozisyondur.

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

Aşağıdaki her çift için is-a, has-a ya da “ikisi de değil” cevaplarından birini seçin:

  • Dog ve Animal
  • Order ve Customer
  • Laptop ve Battery
  • Teacher ve Course
  • AdminUser ve User

is-a seçtiğiniz her çift için “Üst tür beklenen yerde alt tür güvenle kullanılabilir mi?” sorusunu da cevaplayın.

Alıştırma: Override tamamla

return self.width * self.height
Not

Bir alt sınıf area() metodunu yazmazsa, o sınıfın nesnesinde area() çağrılınca NotImplementedError yükselir. Böylece “bu metodu alt sınıf yazmalı” beklentisi kodda açıkça görünür. Yine de Shape soyut (nesnesi oluşturulamayan) bir sınıf olmaz: Shape() ile nesne oluşturulabilir. Nesne oluşturmayı engelleyen soyut temel sınıfı (ABC) gelecek hafta tanıyacaksınız. NotImplemented ile NotImplementedError arasındaki farkı 12. haftada göreceğiz.

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

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

Sıra sizde: Ödeme türleri

Bir Payment üst sınıfı ile CardPayment ve CashPayment alt sınıflarını tasarlayın. İkisinde de pay(amount) metodu olsun. En az bir alt sınıfta super() ile üst sınıfın normal bir metodunu genişletin.

Çalışır kod görevi: İki alt sınıftan birer nesneyi aynı listeye koyun ve bir döngüde her birinin pay() metodunu çağırın. Her nesne için isinstance(nesne, Payment) sonucunu assert ile doğrulayın. Son olarak yanlış kurulmuş bir kalıtım örneği yazın ve yerine geçme ilkesini (LSP) neden bozduğunu iki cümleyle açıklayın.

Sınav provası

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

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. Geliştirici: Ada ve True
  • B. TypeError; pass gövdeli bir sınıf örneklenemez
  • C. AttributeError ve True
  • D. Çalışan: Ada ve True
  • E. Çalışan: Ada ve False

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

  • A. Alt sınıf, üst sınıfın __init__ metodunu kullanabiliyor mu
  • B. Alt sınıf, üst sınıfın bütün metotlarını override ediyor mu
  • C. Alt sınıf, üst sınıfa yeni metotlar ekleyerek onu genişletiyor mu
  • D. İki sınıfta aynı adlı ortak nitelikler bulunuyor mu
  • E. Alt sınıf, üst sınıfın beklendiği her yerde sorunsuz çalışıyor mu

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. Square, kenarları ayrı ayrı değişebilen dikdörtgen sözünü tutamaz
  • D. isinstance(square, Rectangle) çağrısı False verir
  • E. Kalıtım yalnızca aynı sayıda parametre alan kurucularda geçerlidir

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. E-posta: Merhaba
  • C. TypeError; aynı adlı iki metot tanımlanamaz
  • D. Önce Bildirim: Merhaba, sonra E-posta: Merhaba
  • E. None

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]

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
  • B. Alt nesnenin yanında üst sınıftan ayrı bir örnek oluşturmak
  • C. Python’ın 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

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. Kalıtım için tek başına bir gerekçe olmadığını
  • 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

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

  • A. Her seviye nesneye yeni bir kopya eklediği için bellek kullanımı artar
  • B. super() üçten fazla seviyede çalışmaz
  • C. Alt sınıflar üst sınıf metotlarını çağıramaz hâle gelir
  • D. Üst sınıftaki küçük bir değişiklik bütün alt sınıflara yayılır
  • E. Python üç seviyeden derin kalıtıma izin vermez

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

  • A. Kalıtım sınıfları kompozisyondan daha sıkı bağladığı için
  • B. Kalıtım Python’da yavaş çalıştığı için
  • C. Kalıtım kullanılan sınıflar test edilemediği için
  • D. Python’da çoklu kalıtım bulunmadığı için
  • E. Kalıtım standart kütüphane sınıflarına uygun olduğu için

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

class Ticket:
    def price(self):
        return 100

    def label(self):
        return "Tam"

class DiscountTicket(Ticket):
    def price(self):
        return 60

class ChildTicket(DiscountTicket):
    def label(self):
        return "Çocuk"

ticket = ChildTicket()
print(ticket.label(), ticket.price())
  • A. Tam 100
  • B. Çocuk 100
  • C. Çocuk 60
  • D. Tam 60
  • E. AttributeError; ChildTicket sınıfında price yok

1. Doğru: D. Developer gövdesi boş olsa da __init__ ve describe üst sınıftan devralınır. Bir Developer nesnesi aynı zamanda bir Employee sayılır.

  • A yanlış: Developer metodu override etmemiştir; üst sınıfın metni kullanılır.
  • B yanlış: Gövdesi yalnızca pass olan sınıf geçerlidir; nesnesi oluşturulabilir.
  • C yanlış: describe üst sınıfta tanımlıdır ve bulunur.
  • E yanlış: isinstance() üst sınıfları da dikkate alır.

2. Doğru: E. Yerine geçme sorusu (Liskov ilkesi), üst sınıfın beklendiği yerde alt sınıf kullanıldığında sözleşmenin bozulup bozulmadığını sınar.

  • A yanlış: super().__init__() kalıtımla birlikte öğrenildiği için ölçüt gibi görünebilir. Ama kurucuyu kullanabilmek yerine geçme sorusunu cevaplamaz.
  • B yanlış: Override kalıtımla birlikte öğrenildiği için alt sınıfın her metodu yeniden yazması bir ölçüt gibi görünebilir. Ama override isteğe bağlıdır ve yeniden yazılan metot üst sınıfın sözünü bozabilir; yerine geçme sorusunu bu cevaplamaz.
  • C yanlış: Kalıtım “genişletme” diye anlatıldığı için yeni metot eklemek ölçüt gibi görünür. Ama metot eklemek, alt sınıfın üst türün yerine geçip geçemeyeceğini söylemez.
  • D yanlış: Ortak nitelikler iki sınıfın akraba olduğunu düşündürür. Ama iki sınıfta aynı nitelik bulunması, birini ötekinden türetmek için gerekçe olmaz.

3. Doğru: C. Rectangle bekleyen kod, genişliği ve yüksekliği bağımsız ayarlayabileceğini varsayar. Kareyle çalışırken bir kenarı değiştirmek ötekini de değiştirir ve kod şaşırtıcı sonuç alır. is-a cümlesi kurulabilse de yerine geçme bozulur.

  • A yanlış: Kare matematikte bir dikdörtgendir, ama programdaki Rectangle genişliğin ve yüksekliğin ayrı ayrı değişebileceğine söz verir. Sorun bu sözdedir.
  • B yanlış: Kare tek kenar uzunluğuyla tanımlandığı için bazı niteliklerin eksik kalacağı sanılabilir. Ama devralınan nitelikler kaybolmaz; sorun hata değil, beklenmedik davranıştır.
  • D yanlış: Kare ile dikdörtgenin ölçüleri farklı tutulduğu için Python’ın kareyi dikdörtgen saymayacağı sanılabilir. Ama Square sınıfı Rectangle sınıfından türediği için bu çağrı True verir; sorun tür denetiminde değil, davranıştadır.
  • E yanlış: Kare tek, dikdörtgen iki ölçüyle kurulduğu için kurucular uyuşmuyor gibi görünür. Ama alt sınıfın kurucusu üst sınıfınkinden farklı sayıda parametre alabilir.

4. Doğru: B. Alt sınıftaki send üst sınıftaki aynı adlı metodu ezer; Python metodu önce nesnenin kendi sınıfında arar.

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

5. Doğru: E. super().send(message) üst sınıfın send metodunu çalıştırır; alt sınıf dönen metne kendi ekini ekler.

  • 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ış: super() her metotta kullanılabilir; bu kod da bunun örneğidir.
  • D yanlış: Üst sınıfın metni de sonuca girer.

6. Doğru: A. Üst sınıfın __init__ içindeki doğrulama ve atamalar kopyalanmadan kullanılır; kural değişince tek yer güncellenir.

  • B yanlış: super() bir üst sınıf nesnesi veriyormuş gibi okunabilir. Ama kalıtımda ikinci bir nesne oluşmaz; aynı örnek başlatılır.
  • C yanlış: Çoğu örnekte yazıldığı için Python’un bunu zorunlu tuttuğu sanılabilir. Zorunlu değildir; yazılmazsa üst kurulum hiç çalışmaz.
  • D yanlış: super() üst sınıfla bağ kurduğu için override’ı da o sağlıyormuş gibi görünür. Override için aynı adla metot yazmak yeter; super() gerekmez.
  • E yanlış: super() çağrısından sonra nesne hazır göründüğü için alt sınıfın nitelikleri de kurulmuş sanılabilir. Ama alt sınıfın kendi nitelikleri ayrıca yazılır.

7. Doğru: B. Ortak bir alan bulunması is-a ilişkisi kurmaz. Driver is a Car cümlesi anlamsızdır; ortak nitelik yalnız bir rastlantıdır.

  • A yanlış: Aynı alanı iki kez yazmamak için tek sınıf kısa yol gibi görünür. Ama sürücü ile arabanın sorumlulukları ayrıdır.
  • C yanlış: Ortak nitelik, bir sınıfın ötekinden türeyebileceğini düşündürür. Ama sürücü bir araba değildir.
  • D yanlış: Ortak alanı tek yerde toplamak tekrarı azaltıyor gibi görünür. Ama yalnızca aynı adlı bir nitelik taşımaları, ortak bir üst tür gerektirmez.
  • E yanlış: Arabanın bir sürücüsü olabileceği için bu seçenek doğru bir ilişkiyi anlatıyor gibi görünür. Gerekebilir ama bunu name alanının ortaklığı söylemez.

8. Doğru: D. Bağ sıkıdır; değişikliğin nereleri etkilediği izlenemez hâle gelir. Bu yüzden bu derste iki-üç seviyeden derin hiyerarşi tasarlanmaz ve kompozisyon tercih edilir.

  • A yanlış: Her seviye ayrı bir nesne kuruyormuş gibi düşünülürse bellek sorunu akla gelir. Ama kalıtımda tek bir nesne vardır; sorun bellekte değil bakımdadır: bir değişikliğin nereleri etkilediğini izlemek zorlaşır.
  • B yanlış: Zincir uzadıkça super() çağrılarının kopacağı sanılabilir. Ama her sınıf super() çağırdıkça çağrı zincir boyunca ilerler.
  • C yanlış: Uzun zincirde en üstteki sınıf uzakta kalmış gibi görünür. Ama alt sınıflar üst sınıf metotlarını çağırabilir; sorun, alt sınıfların üst sınıfa bu kadar sıkı bağlı olmasıdır.
  • E yanlış: Derste iki-üç seviye sınırı konduğu için bunun bir dil kuralı olduğu sanılabilir. Teknik bir sınır yoktur; sınır tasarım kararıdır.

9. Doğru: A. Alt sınıf üst sınıfın iç yapısına bağlıdır; üst sınıftaki değişiklik alt sınıfları da etkiler. Kompozisyonda bir parçayı değiştirmek için onu tutan niteliğe başka bir nesne atamak yeter, sınıfın kendisine dokunmak gerekmez. Üst ve alt sınıf sayısı arttıkça tasarım iyileşmez.

  • B yanlış: Metot üst sınıflarda arandığı için kalıtımın programı yavaşlattığı sanılabilir. Ama uyarının gerekçesi hız değil, sınıflar arasındaki bağın sıkılığıdır.
  • C yanlış: Alt sınıf üst sınıfa bağlı olduğu için ayrı sınanamayacağı sanılabilir. Ama kalıtım kullanılan sınıflar da test edilebilir. Sorun, bağın sıkı olmasıdır.
  • D yanlış: Çoklu kalıtım bu derste sınavda sorulmadığı için Python’da hiç yokmuş gibi görünebilir. Ama Python çoklu kalıtımı destekler ve uyarının bununla ilgisi yoktur.
  • E yanlış: Uyarı kalıtımı sınırladığı için onu hazır sınıflara bırakmak gerekiyormuş gibi görünebilir. Ama kendi sınıflarımızda da is-a ilişkisi varsa kalıtım yerindedir.

10. Doğru: C. Metot önce nesnenin kendi sınıfında, bulunamazsa bir üst sınıfta aranır. label ChildTicket içinde override edildiği için oradan gelir. price ChildTicket içinde yok; arama DiscountTicket sınıfında durur ve 60 döner.

  • A yanlış: İki metot da ilk kez Ticket sınıfında tanımlandığı için ikisinin de oradan geleceği düşünülebilir. Ama alt sınıflarda override edilen metotlar üst sınıftakilerin önüne geçer.
  • B yanlış: ChildTicket içinde bulunmayan metodun en üstteki Ticket sınıfından geldiği düşünülebilir. Ama arama bir üst sınıftan başlar; DiscountTicket kendi price metodunu tanımladığı için Ticket sınıfına hiç bakılmaz.
  • D yanlış: label metodunun ilk tanımlandığı sınıftan geleceği sanılabilir. Ama ChildTicket bu metodu override ettiği için nesnenin kendi sınıfındaki label çalışır.
  • E yanlış: ChildTicket gövdesinde yalnız label yazdığı için price metodunun bulunamayacağı sanılabilir. Ama alt sınıf, üst sınıflarının metotlarını devralır; price DiscountTicket sınıfından gelir.

Tek sayfa özet

  • Kalıtımda alt sınıf, üst sınıfın niteliklerini ve metotlarını yeniden yazmadan devralır. Bu bir is-a ilişkisidir.
  • Bir metot önce nesnenin kendi sınıfında aranır, orada yoksa üst sınıftan gelir.
  • isinstance() alt sınıfın nesnesini üst sınıfın örneği olarak da tanır.
  • Alt sınıf aynı adla bir metot yazarak üst sınıftaki metodu override eder. Üst sınıftaki metot bu durumda kendiliğinden çağrılmaz.
  • super() üst sınıfın metodunu çağırır. Kurucuda üst sınıfın kurulumunu, normal bir metotta üst sınıfın sonucunu kopyalamadan kullanırsınız.
  • “Bir … türüdür” cümlesi kalıtım kararını tek başına vermez. Üst sınıfın beklendiği yerde alt sınıf, sözleşmeyi bozmadan çalışmalıdır (Liskov yerine geçme ilkesi). Square ve Rectangle bu sözün bozulduğu bir örnektir.
  • Ortak bir nitelik kalıtım gerekçesi olmaz. “Sahiptir” cümlesi daha doğru duruyorsa kompozisyon seçin.
  • Derin kalıtım zincirinde üst sınıftaki küçük bir değişiklik bütün alt sınıflara yayılır.

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 üst sınıfın bir alt türü sayıldığını 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 bir niteliği kalıtım gerekçesi saymaz.
  • Derin kalıtım zincirinin kırılganlığını açıklar.
  • Override edilen ve devralınan metotların çağrıda hangi sınıftan geldiğini kod izleyerek belirler.
Başa dön