2.22. Kalıtım

Kalıtım, bir sınıfın başka bir sınıfı genişletmesine olanak tanır – onun tüm yöntemleri ve nitelikleriyle başlar, ardından farklı olanı ekler veya geçersiz kılar. Orijinaline taban (veya ebeveyn) denir; genişletilmiş haline ise alt sınıf denir.

Aşağıda bir Square alt sınıfına işaret eden bir Shape taban sınıfı; Square bir yöntemi (area) geçersiz kılar ve gerisini miras alır.

Square, Shape üzerindeki her yöntemi yeniden kullanır ve özelleştirmesi gerekenleri geçersiz kılar.

2.22.1. Bir alt sınıf tanımlama

Taban sınıf, class satırındaki parantezlerin içine yazılır:

class Shape:
    def __init__(self, name):
        self.name = name

    def describe(self):
        return self.name + " with area " + str(self.area())

    def area(self):
        return 0

class Square(Shape):
    def __init__(self, side):
        super().__init__("square")
        self.side = side

    def area(self):
        return self.side * self.side

s = Square(4)
print(s.describe())

Çıktı:

square with area 16

Square, describe yöntemini Shape‘ten değiştirilmeden miras alır ve gerçek bir değer döndürmek için area yöntemini geçersiz kılar. Tabanın describe yöntemi yine de çalışır çünkü self.area() çağrısı yapar – bu da çalışma zamanında geçersiz kılınan sürüme çözümlenir.

2.22.2. super()

super(), bir yöntemin bir yöntemin taban sınıf sürümünü çağırmasına olanak tanıyan bir vekil döndürür. En yaygın kullanım, tabanın kendi niteliklerini ilkleyebilmesi için bir alt sınıfın __init__ yönteminden tabanın __init__ yöntemini çağırmaktır:

class Square(Shape):
    def __init__(self, side):
        super().__init__("square")    # run Shape.__init__
        self.side = side

super().__init__ yöntemini unutmak yaygın bir hata kaynağıdır: tabanın ayarlayacağı nitelikler hiçbir zaman ayarlanmaz ve bunlara dayanan miras alınan yöntemler daha sonra çöker.

2.22.2.1. Örtük taban sınıf

Her sınıf örtük olarak Python’un tür hiyerarşisinin kökü olan object sınıfından miras alır. __repr__, __eq__ gibi yöntemler ve nitelik erişiminin arkasındaki mekanizma, bir sınıf hiçbir taban bildirmese bile oradan gelir. Açıkça class Foo(object): yazmak ile class Foo: yazmak modern Python’da eşdeğerdir; ikincisi geleneksel biçimdir.

2.22.3. Kalıtım ne zaman yardımcı olur – ve ne zaman olmaz

Kalıtımı, bir sınıf gerçekten başka bir sınıfın daha özel bir türü olduğunda ve davranışın çoğunu paylaştığında kullanın. Klasik test “-dır” kontrolüdür: bir Square bir Shape‘tir.

İki sınıf yalnızca tesadüfen birkaç yardımcıyı paylaşıyorsa, kalıtım gereğinden fazladır. Bunun yerine bileşim kullanın: bir sınıfın diğerinin bir örneğini bir nitelik olarak tutmasını ve onu bu nitelik aracılığıyla kullanmasını sağlayın. Biçimi “-dır” değil, “-e sahiptir” şeklindedir:

class Logger:
    def log(self, msg):
        print("[log]", msg)

# inheritance: Worker IS-A Logger
class WorkerInherits(Logger):
    def run(self):
        self.log("starting")

# composition: Worker HAS-A Logger
class WorkerComposes:
    def __init__(self):
        self.logger = Logger()

    def run(self):
        self.logger.log("starting")

Her ikisi de çalışır. Bileşim sürümü genellikle daha iyidir çünkü:

  • Arayüzleri küçük tutar. WorkerComposes yalnızca run ve logger öğelerini açığa çıkarır. WorkerInherits ayrıca log öğesini de açığa çıkarır – çağıranlar amaçlanmış olsun ya da olmasın doğrudan worker.log(...) yazabilir.

  • Yaşam sürelerini ayrıştırır. Günlükleyici, Worker sınıfına hiç dokunmadan değiştirilebilir, çalışanlar arasında paylaşılabilir veya çalışan başına farklı şekilde oluşturulabilir. Bir alt sınıf, tabanına cıvatalanmıştır.

  • Yöntem-adı çakışmalarını önler. İkisi de run tanımlayan iki taban, bir sınıf her ikisinden de miras almaya çalıştığında çakışır; iki nitelik asla çakışmaz.

Pratik bir genel kural:

  • Alt sınıf gerçekten tabanın bir türü olduğunda ve tabandaki her yöntem onun için de anlamlı olduğunda kalıtımı kullanın – Square bir Shape‘tir; MemoryError bir Exception‘dır.

  • Diğer her şey için bileşimi kullanın – yalnızca başka bir sınıfın kimliğini değil, davranışını istediğinizde. Çoğu gerçek kod, bileşimi kalıtımdan çok daha fazla kullanır.