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ı
NoteSınav dışı (bilgi amaçlı)
ç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.
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.
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
returnself.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.
E. Aynı adlı bir davranışın hangi sırayla aranacağını
TipCevaplar ve gerekçeler
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.