class Page(Content) yazdığınızda inheritance (kalıtım) gereği, Content sınıfı, Page sınıfının parent sınıfı (ebeveyn) oluyor. Inheritance, benzer özellikler taşıyan ve “is-a” ilişkisini sağlayan (“Page is a Content”) sınıfları birbirine bağlayarak kod tekrarını ortadan kaldırır. Yani yukarıdan, parent’dan ne var ne yok (attribute (nitelik), metot) miras edinirsiniz (inherit).
Ama olay burada bitmiyor tabii; Content’teki her şeyi miras alıp başka bir şey yapmayacaksak neden yeni bir sınıf (Page) yazıyoruz? Dolayısıyla alt sınıfın, üst sınıftan aldıklarının yanında masaya koyacağı başka şeyler de olmalıdır. Sizin örneğinizde bunu date niteliği ile görüyoruz. Onu super’e yollamayıp self. ile ilklendirmenizden şunu anlıyoruz: Content nesneleri normalde date’e sahip değildir, ama aradığımız, ortak olarak paylaşmak istediğimiz birçok şeye sahiptir (title, body). O zaman bu ortak olanları onun sayesinde ilklendirip kendi niteliğimiz yapalım, bu farklı olanı (yani date’i, yani bu yeni sınıfı yazma dürtülerimizden biri olan niteliği) kendimiz alt sınıftaki __init__'in içerisinde self.date = ... diye halledelim.
Alt sınıfın metotlar üzerine de koyacağı şeyler olabilir. Önce üzerine koymak istemeyeceği, direkt miras alacağı, kod tekrarından kaçınmayı sağlayacağı bir örnekten başlayalım. Mesela başlık ve body’deki toplam karakter sayısını merak ediyoruz. Üst sınıf Content sınıfı şöyle bir metoda sahip:
# Content sınıfının içerisindeyiz
def karakter_sayisi(self):
return len(self.title + self.body)
Bu metodun üzerine alt sınıf Page’in katacağı bir şey yok; bu metodu tekrar Page sınıfında bir daha yazmayız. Content “olan” (Page “is a” Content) her “şey” (buradan polymorphism’e yelken açarsınız) bu metodu miras edip direkt kullanır.
Şimdi ise alt sınıfın, üstte bulunmayan ve kendine has niteliklerini kullanan bir metodunu yazarak masaya yeni bir şey koyalım (tekrar: yeni bir (alt) sınıf yazmamızın dürtülerinden birisi). Mesela bir Page nesnesinin oluşturulma tarihinin (.date) üzerinden kaç gün geçtiğini merak ediyoruz. Content sınıfında böyle bir metot yok, olamaz: genelgeçer Content objelerinin .date diye bir niteliği yok zira. Page sınıfında biz kendimiz yazarız bu metodu, miras almayız, üst sınıfı “extend” ederiz:
# Page sınıfının içerisindeyiz
def kac_zaman_oldu(self):
return datetime.datetime.now() - self.date
Bu artık her Content objesinde bulunmaz, Page (ve Page’den inherit edenlerin!) objelerinde bulunur.
Bir diğer durum da, halihazırda Content sınıfında bulunan bir metodun Page sınıfından (iyi nedenlerden dolayı :p) tekrar yazılmasıdır, override edilmesidir. Bunu mesela __str__ üzerinden görebiliriz. __str__ metodu bir objenin insan-okuyabilir temsilini döndürmeye yarayabilir. Content sınıfında bunun bir örneği şöyle olabilir:
# Content sınıfının içerisindeyiz
def __str__(self):
return f"{self.title}\n{'*' * 40}\n{self.body}\nSON"
Her Contentte title ve body vardı ya, bu da onları kullanarak bir string temsili oluşturuyor Content nesneleri için. Ama Page nesneleri bununla yetinmeyip kendilerine has .date niteliğini de kullanmak isteyebilirler. Üstteki temsilin önünde tarih de yazsın. O zaman:
# Page sınıfının içerisindeyiz
def __str__(self):
content_str = super().__str__()
return f"Tarih = {self.date}\n{content_str}"
Burada Content’in __str__'sinin sağladığını önce elde ediyoruz, sonra .date’i işin içine katıp geriye bir string döndürüyoruz. Hem Content’te hem de Page’de artık __str__ metodu var. Eğer Page objesi __str__ metodunu çağırırsa (mesela str(Page(...)) ile), ona has olan çalışacaktır “override” ettiğimiz için. Ama düz bir Content objesi (veya Content’i parent belleyip de bu metodu override etmeyen bir sınıfın objesi) üzerinden __str__ çağrılırsa, Content sınıfındaki __str__ çalışır.
Velhasıl, bir sınıf, bir başka sınıfı miras aldığında, o sınıfın tüm fonksiyonalitesine erişebildiği gibi bunların üzerine de koyabiliyor (extend edebiliyor); daha doğrusu üstüne koyması bekleniyor, öbür türlü inheritance’a gerek yoktur. Aslında bu miras alan sınıfa, sizdeki Page, alt sınıf yerine belki üst sınıf (super class) desek daha doğru olabilir (ama terminoloji ekseriyetle öbür yönde); çünkü parent’in sağladığının üzerine çıkıyor, boynuz kulağı geçmiş oluyor. “Hepsi ve daha fazlası” diyebiliriz… (Ebeveyn sınıfın halka açık olarak (public) sunduğu tüm fonksiyonaliteye erişebiliyor, özel olanlara (private) programlama diline bağlı olarak erişemeyebilir; Python’da private nitelik/metot olmadığından ne var ne yok erişilebiliyor.)