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

NoteSı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.

Personel programındaki beş işi C’de nasıl yapardınız?

Bir şirketin personel programını yazıyorsunuz. 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. Program şu beş işi yapacak:

  1. Yazılımcının adını tutmak ve onu tanıtmak, ama çalışan için yazılmış kodu kopyalamamak
  2. Yöneticiyi başka bir unvanla tanıtmak: Yönetici: Can
  3. Yazılımcının programlama dilini de tutmak ve onu diliyle tanıtmak: Çalışan: Ada (Python)
  4. Bütün personeli, yazılımcısıyla ve yöneticisiyle birlikte, tek bir fonksiyonla tanıtmak
  5. Şirket arabalarını da kaydetmek: her arabanın bir adı var ve onu bir çalışan kullanıyor

Programlama Temelleri’nde C ile yazdığınız struct’ları ve fonksiyonları düşünün. Bu işlerin her birini C’de nasıl yapardınız? Okumaya devam etmeden önce her iş için bir satır yazın. Bu haftanın Python konusunu bilmeniz gerekmiyor: “struct’ı kopyalayıp yeni bir alan eklerim” de bir cevap.

Bölümde bu beş işin Python’da nasıl yazıldığını göreceğiz. Bölümün sonunda listenize döneceğiz.

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

Birinci iş: yazılımcı, çalışanın kodunu kopyalamadan adını tutsun ve kendini tanıtsın. 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

İkinci iş: yönetici başka bir unvanla tanıtılsın. 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

Üçüncü iş: yazılımcı programlama dilini de tutsun. 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

Dördüncü iş: bütün personeli tek fonksiyonla tanıtmak. 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

Beşinci iş: şirket arabaları. 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.

WarningKalı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.

Soruya dönelim: personel programı Python’da

Bölümün başında personel programının beş işini C’de nasıl yapacağınızı sormuştuk. Python’daki karşılıkları şöyle:

İş 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 arabalarını 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

Listenizi tabloyla karşılaştırın. Birinci iş için Employee alanlarını yeni bir struct’a kopyaladıysanız, çalışana yeni bir alan eklendiğinde iki yeri birden değiştirmeniz gerekirdi. Kalıtımda alan ve metot tek yerde durur. İkinci ve dördüncü iş için tür alanı ve switch yazdıysanız, Python’da bu dallanmayı hiç yazmadığımıza dikkat edin: hangi describe() metodunun çalışacağını nesnenin sınıfı seçti. Gelecek hafta bu fikre polimorfizm adıyla döneceğiz. Beşinci iş için arabaya sürücüyü gösteren bir alan koyduysanız, C’de de kompozisyon kurmuşsunuz.

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
Note

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 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. 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

Madde 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ıfla aynı dosyada mı
  • 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

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. Square, kenarları ayrı ayrı değişebilen dikdörtgen sözünü tutamaz
  • 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. E-posta: Merhaba
  • C. TypeError; aynı adlı iki metot tanımlanamaz
  • D. Önce Bildirim: Merhaba, sonra 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
  • B. Alt nesnenin yanında üst sınıftan ayrı bir örnek oluşturmak
  • 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. 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

Madde 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

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

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

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

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

    def channel(self):
        return "genel"

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

n = SmsNotification()
print(n.send("Ders 10.00"))
print(n.channel())
  • A. Önce Bildirim: Ders 10.00, sonra SMS: Ders 10.00, sonra genel
  • B. Önce Bildirim: Ders 10.00, sonra genel
  • C. Önce SMS: Ders 10.00, sonra genel
  • D. Önce SMS: Ders 10.00, sonra AttributeError
  • E. Önce SMS: Ders 10.00, sonra None

Madde 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.

Madde 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ış: Yan yana yazılan sınıflar arasında bir bağ varmış gibi görünür. Ama sınıfların hangi dosyada durduğu tür ilişkisini değiştirmez.
  • 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.

Madde 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ış: Sorun bir dil kısıtı gibi görünebilir. Böyle bir kısıt yoktur; sorun tasarımdadı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.

Madde 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.

Madde 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.

Madde 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.

Madde 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.

Madde 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.

Madde 9. Doğru: A. 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 yalnız 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.

Madde 10. Doğru: C. send alt sınıfta override edildiği için alt sınıftaki çalışır. channel alt sınıfta yok, üst sınıftan devralınır.

  • A yanlış: Override üst sınıftaki metodu kendiliğinden çağırmaz. Bunun için super().send(...) yazmak gerekir.
  • B yanlış: Metot önce nesnenin kendi sınıfında aranır. SmsNotification kendi send metoduna sahip.
  • D yanlış: Alt sınıfta tanımlanmayan metot hata vermez, üst sınıftan gelir. Kalıtımın amacı bu.
  • E yanlış: Devralınan metot üst sınıftaki gövdesiyle çalışır ve "genel" döndürür.

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.
Back to top