Nesne Tabanlı Programlama 2

3 - Kalıtım (Inheritance)

Emre Can Yılmaz

Ondokuz Mayıs Üniversitesi

2026

Kalıtım: Motivasyon ve Hedefler

Bu hafta, bir sınıftan başka bir sınıf türeterek ortak davranışları yeniden kullanmayı ve alt sınıflarla sınıfı daha özel hale getirmeyi ele alacağız.

Dersin sonunda şunları yapabiliyor olmanız beklenir:

  • Taban sınıf ve alt sınıf ilişkisini doğru kurmak
  • Alt sınıfta __init__ yazarken super() ile taban kurulumunu sürdürmek
  • Override (yeniden tanımlama) ile genişletme (extend) yaklaşımını ayırt etmek
  • Polimorfizmi örnekleyebilmek
  • Çoklu mirasta MRO ve super() zincirinin nasıl çalıştığını görmek
  • isinstance() ve issubclass() ile tür ilişkisini kontrol etmek

Temel Terimler

  • Kalıtım (Inheritance): Mevcut bir sınıftan (taban sınıf) yeni bir sınıf (alt sınıf) türetme mekanizması.
  • Taban sınıf (Base class): Ortak nitelik ve metotların tanımlandığı sınıf.
  • Alt sınıf (Derived class): Taban sınıftan miras alır; yeni nitelik/metot ekleyebilir veya davranışı değiştirebilir.
  • Override (yeniden tanımlama): Taban sınıftaki bir metodu alt sınıfta aynı adla yeniden yazmak.
  • Genişletme (extend): Taban sınıfın davranışını koruyup üzerine ek davranış eklemek (çoğunlukla super() ile).

Basit Örnek: Person ve Student

class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def print_name(self):
        print(self.first_name, self.last_name)

# Taban sınıf örneği
p = Person("John", "Doe")
p.print_name()  # John Doe

# Student sınıfı, Person'dan miras alıyor
class Student(Person):
    pass  # Şimdilik ek bir şey yapmıyoruz.

s = Student("Ali", "Veli")
s.print_name()  # Ali Veli

pass notu

pass, “bu blok şimdilik boş; hata verme, devam et” anlamına gelir.

Önemli not

Bu sunumdaki kod parçaları bağımsız örneklerdir. Aynı dosyada ardışık çalıştırmak yerine, örnekleri tek tek değerlendirin.

Alt Sınıfta __init__ Yazmazsak Ne Olur?

Alt sınıf __init__ tanımlamazsa, Python taban sınıfın __init__ metodunu kullanır.

class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

class Student(Person):
    pass

s = Student("Ali", "Veli")  # Person.__init__ çalışır
print(s.first_name, s.last_name)

Alt Sınıfta __init__ Yazınca: Neden super()?

Alt sınıf __init__ tanımladığınız anda, taban sınıfın __init__ metodu otomatik olarak çalışmaz.
Bu nedenle taban sınıfın yaptığı kurulum (ör. first_name, last_name gibi niteliklerin oluşturulması) kaybolabilir.

Bu kurulumu korumak için taban sınıfın __init__ metodunu açıkça çağırırız. Python’da bunun standart yolu super().__init__(...) kullanmaktır.

Ne olur çağırmazsak? Tabanın oluşturması beklenen nitelikler oluşmayabilir ve daha sonra erişimde AttributeError görülebilir.

class Student(Person):
    def __init__(self, first_name, last_name, year):
        self.graduation_year = year  # super() yok

s = Student("Ali", "Veli", 2023)
print(s.first_name)  # AttributeError (first_name yok)

Doğrudan Taban Sınıf Çağrısı (Gösterim Amaçlı)

class Student(Person):
    def __init__(self, first_name, last_name, year):
        Person.__init__(self, first_name, last_name)
        self.graduation_year = year

s = Student("Ali", "Veli", 2023)
s.print_name()
print(s.graduation_year)

Uyarı

Bu yöntem çoklu mirasta kırılgan olabilir. Python’da genel tercih super() kullanmaktır.

super() ile __init__() Kullanımı

super(), alt sınıf içinden taban sınıfta tanımlı metotları çağırmak için kullanılır. Bu derste şimdilik super()’ı “taban sınıfın kurulumunu/metodunu çağırma” olarak düşünebilirsiniz; çoklu mirasta ayrıntısını ayrıca göreceğiz.

class Student(Person):
    def __init__(self, first_name, last_name, year):
        super().__init__(first_name, last_name)
        self.graduation_year = year

s = Student("Ali", "Veli", 2023)
s.print_name()
print(s.graduation_year)

Override: Metodu Yeniden Tanımlama

Override (yeniden tanımlama), alt sınıfın taban sınıfta bulunan bir metodu aynı adla yeniden yazarak kendi davranışını tanımlamasıdır. Alt sınıf nesnesinde bu metot çağrıldığında, tabandaki sürüm yerine alt sınıftaki sürüm çalışır.

class Person:
    def greet(self):
        print("Hello")

class Student(Person):
    def greet(self):
        print("Hi, I'm a student!")

Student().greet()

Genişletme: Taban Davranışını Koruyup Üzerine Eklemek

Genişletme (extend), taban sınıftaki metodu tamamen değiştirmek yerine, tabanın yaptığı işi koruyup üzerine yeni adımlar eklemektir. Bu yaklaşımda çoğunlukla super() ile taban sınıfın metodu çağrılır, ardından alt sınıfa özgü ek davranış eklenir.

class Person:
    def greet(self):
        print("Hello")

class Student(Person):
    def greet(self):
        super().greet()
        print("I'm a student.")

Student().greet()

Polimorfizm: Aynı Metot, Farklı Davranış

Kalıtımın önemli çıktılarından biri polimorfizmdir: aynı metot adı, farklı sınıflarda farklı davranış üretir.

class Person:
    def greet(self):
        print("Hello, I'm a person.")

class Student(Person):
    def greet(self):
        print("Hello, I'm a student.")

class Teacher(Person):
    def greet(self):
        print("Hello, I'm a teacher.")

people = [Person(), Student(), Teacher()]
for x in people:
    x.greet()

Ne Zaman Kalıtım? (Kısa Rehber)

Kalıtım çoğunlukla şu ilişki doğruysa anlamlıdır:

  • “X, Y’nin bir türü mü?” Örnek: Student, Person türüdür.

Buna karşılık, şu ilişki daha çok bileşim (composition) gerektirir:

  • “X’in bir Y’si var mı?” Örnek: Car bir Engine türü değildir; motor araçta bir parçadır.

isinstance() ve issubclass() Nedir?

Bu iki fonksiyon, kalıtım ilişkilerini kontrol etmek için kullanılır:

  • isinstance(nesne, Sinif): “Bu nesne, bu sınıftan (veya onun alt sınıflarından) mı?”
  • issubclass(AltSinif, UstSinif): “Bu sınıf, bu sınıfın alt sınıfı mı?”

Ek not

isinstance(nesne, (A, B)) biçiminde birden fazla sınıfı aynı anda kontrol edebilirsiniz.

isinstance() / issubclass() Örnekleri

s = Student("Ali", "Veli", 2023)

print(isinstance(s, Student))  # True
print(isinstance(s, Person))   # True
print(isinstance(s, object))   # True

print(issubclass(Student, Person))  # True
print(issubclass(Person, Student))  # False

Örnek Model: Oyuncu Sınıfı

Bu örnekte:

  • Ortak yapı taban sınıfta tutulur.
  • Alt sınıflar bazı değerleri farklılaştırır (ör. guc).
  • Metotlar nesnenin durumunu (ör. puan) günceller.

Taban Sınıf: Oyuncu

class Oyuncu:
    def __init__(self, isim, unvan):
        self.isim = isim
        self.unvan = unvan
        self.guc = 0
        self.puan = 0

    def hareket_et(self):
        print("Hareket ediyor...")

    def puan_kazan(self, miktar=10):
        self.puan += miktar
        print(f"Puan +{miktar} -> {self.puan}")

    def puan_kaybet(self, miktar=5):
        self.puan -= miktar
        print(f"Puan -{miktar} -> {self.puan}")

Alt Sınıflar: Daha Özel Hale Getirme

Burada “daha özel” derken, taban sınıfın genel tanımına ek özellik/kısıt ekleyerek daha dar bir tür tanımlamayı kastediyoruz.

class Asker(Oyuncu):
    def __init__(self, isim, unvan):
        super().__init__(isim, unvan)
        self.guc = 100

    def hareket_et(self):
        super().hareket_et()
        print("Hedefe ulasti.")

class Isci(Oyuncu):
    def __init__(self, isim, unvan):
        super().__init__(isim, unvan)
        self.guc = 70

class Yonetici(Oyuncu):
    def __init__(self, isim, unvan):
        super().__init__(isim, unvan)
        self.guc = 50

Kullanım Örneği

asker = Asker("Ahmet", "Binbasi")
isci = Isci("Mehmet", "Usta")
yonetici = Yonetici("Selim", "Mudur")

print(asker.isim, asker.unvan, asker.guc)  # Ahmet Binbasi 100
asker.hareket_et()

isci.puan_kazan(20)
yonetici.puan_kaybet(15)

print(isinstance(asker, Oyuncu))  # True
print(issubclass(Asker, Oyuncu))  # True

Çoklu Miras ve MRO

Python’da bir sınıf birden fazla sınıftan miras alabilir.

  • Çoklu mirasta, bir metot/nitelik arandığında sınıfların hangi sırayla kontrol edileceği MRO (Method Resolution Order) ile belirlenir.
  • Bir metot çağrıldığında, Python sınıflara bu sıraya göre bakar.
  • Bu sırayı SomeClass.mro() veya SomeClass.__mro__ ile görebiliriz.
  • Çoklu mirasta parantez içindeki sınıf sırası değişirse MRO da değişir. Örneğin FlyingCar(Flyable, Car) ile FlyingCar(Car, Flyable) farklı sıralar üretir.

MRO: Kısa Örnek

class A: pass
class B(A): pass
class C(B): pass

print([x.__name__ for x in C.mro()])
# ['C', 'B', 'A', 'object']

Çoklu Mirasta super() Nasıl Çalışır?

Çoklu mirasta super() çoğu zaman “doğrudan ebeveyn” anlamına gelmez.

super(), MRO sırasına göre bir sonraki sınıfa geçer.

Bu sayede sınıflar bir zincir gibi sırayla çalışabilir.

Çoklu Mirasta super() Zinciri (İşbirlikçi Yaklaşım)

Amaç, MRO zincirindeki sınıfların her birinin katkı yapabilmesidir.

Bu yüzden (özellikle aynı metot adı etrafında) her sınıf uygun yerde super() çağırarak zinciri sürdürür.

Örnek: Car, Flyable, FlyingCar

Bu örnekte:

  • go() ve fly() ayrı davranışlar olarak miras alınır.
  • start() ise zincir mantığıyla, MRO üzerinden katkıların sıralanmasını gösterir.
class Startable:
    def start(self):
        print("Start sequence:")

class Car(Startable):
    def go(self):
        print("Going")

    def start(self):
        super().start()
        print(" - engine on")

class Flyable(Startable):
    def fly(self):
        print("Flying")

    def start(self):
        super().start()
        print(" - rotors on")

class FlyingCar(Flyable, Car):
    def start(self):
        super().start()

Çalıştırma ve Çıktı

fc = FlyingCar()

fc.go()     # Going
fc.fly()    # Flying

fc.start()
# Start sequence:
#  - engine on
#  - rotors on

print([c.__name__ for c in FlyingCar.mro()])

Zincir Nasıl Kopar? (Kısa Not)

Eğer aradaki bir sınıf, ilgili metotta super() çağırmazsa zincir MRO boyunca ilerlemez ve bazı sınıfların katkısı devreye girmeyebilir.

Miras Kullanım Biçimleri

  1. Olduğu gibi aktarma: pass
  2. Override: tabandaki metodu yeniden tanımlama
  3. Genişletme: taban davranışını koruyup üzerine ekleme (super() ile)
  4. Yeni nitelik/metot ekleme: alt sınıfı zenginleştirme

Ek (İleri Seviye): *args / **kwargs ile Parametre Aktarımı

class Student(Person):
    def __init__(self, *args, year, **kwargs):
        super().__init__(*args, **kwargs)
        self.graduation_year = year

s = Student("Ali", "Veli", year=2023)
s.print_name()
print(s.graduation_year)

Not: year burada keyword-only argümandır; çağrıda year=... yazmak gerekir.

Alıştırmalar

Alıştırma 1: Override

Amaç: Alt sınıfta bir metodu override ederek davranışı değiştirmek.

Görev:

  • Kisi adında bir taban sınıf yazın:
    • Nitelikler: ad, soyad
    • Metot: yazdir() → ekrana ad soyad yazsın.
  • Ogretmen(Kisi) adında bir alt sınıf yazın:
    • Ek nitelik: brans
    • yazdir() metodunu override edin → ekrana ad soyad - brans yazsın.

Kontrol kodu:

o = Ogretmen("Ayse", "Yilmaz", "Matematik")
o.yazdir()

Beklenen çıktı:

Ayse Yilmaz - Matematik

Alıştırma 2: Çoklu Miras ve MRO

Görev:

  • Araba sınıfı yazın:

    • baslat()Araba basladi yazsın.
  • Tekne sınıfı yazın:

    • baslat()Tekne basladi yazsın.
  • AmfibiArac(Araba, Tekne) sınıfını yazın.

  • AmfibiArac().baslat() hangi metodu çalıştırıyor gözlemleyin.

  • AmfibiArac.mro() çıktısını yazdırın.

Kontrol kodu:

a = AmfibiArac()
a.baslat()
print([c.__name__ for c in AmfibiArac.mro()])

Beklenen:

  • baslat() çağrısında hangi sınıfın metodu çalışıyorsa onu görmelisiniz.
  • mro() listesinde sınıfların sırası yazmalıdır.

Alıştırma 3: Polimorfizm

Amaç: Aynı metot adının farklı sınıflarda farklı davranış üretmesini görmek.

Görev:

  • Oyuncu taban sınıfını yazın:
    • ad niteliği tanımlayın.
    • Metot: yetenek_kullan()Oyuncu yetenek kullandi yazsın.
  • Savasci(Oyuncu), Buyucu(Oyuncu), Okcu(Oyuncu) alt sınıflarını yazın:
    • yetenek_kullan() metodunu override edin ve her biri farklı mesaj yazsın.
  • Bu nesneleri oyuncular listesine koyup döngüyle yetenek_kullan() çağırın.

Kontrol kodu:

oyuncular = [Savasci("Ali"), Buyucu("Veli"), Okcu("Ayse")]
for o in oyuncular:
    o.yetenek_kullan()

Beklenen:

  • Üç satır çıktı olmalı ve her satır sınıfa göre farklı olmalı.

Alıştırma 4: Extend ve super()

Amaç: Taban davranışını koruyup üzerine ek davranış eklemek (extend).

Görev:

  • Kisi taban sınıfı:

  • yazdir()ad soyad yazsın.

  • Ogrenci(Kisi) alt sınıfı:

  • Ek nitelik: numara

  • yazdir() metodunu override edin ama genişletme yapın:

    • Önce super().yazdir() çağırın,
    • Sonra Numara: ... yazdırın.

Kontrol kodu:

o = Ogrenci("Ali", "Veli", 123)
o.yazdir()

Beklenen çıktı:

Ali Veli
Numara: 123

Sonuç

  • Kalıtım, doğru ilişki kurulduğunda kodun yeniden kullanımını ve sınıfların daha özel hale gelmesini sağlar.
  • Override ve genişletme ayrımı, tasarım kararlarını daha açık hâle getirir.
  • Çoklu mirasta MRO, metot arama sırasını belirler; super() bu sıraya göre “bir sonraki” sınıfa geçer.
  • isinstance() ve issubclass() kalıtım ilişkilerini kontrol etmek için kullanılır.