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 Content
te 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.)