Nesne Tabanlı Programlama 2

6 - Özel (Magic) Metotlar ve Operatör Aşırı Yükleme

Emre Can Yılmaz

Ondokuz Mayıs Üniversitesi

2025

İçerik

  • Özel (Magic) Metotlar Nedir?
  • Temel Özel Metotlar
    • __init__, __str__, __repr__, __len__
    • __getitem__, __setitem__, __delitem__
  • Operatör Aşırı Yükleme (Operator Overloading)
    • Aritmetik: __add__, __sub__, __mul__, vb.
    • Karşılaştırma: __eq__, __lt__, vb.
  • Ek Özel Metotlar
    • __contains__, __call__, __iter__, __enter__/__exit__
  • Gerçek Dünya Örnekleri

Özel (Magic) Metotlar Nedir?

  • Python’da çift alt çizgi (__) ile başlayıp biten metotlar.
  • Sınıfların Python’un yerleşik özellikleriyle (ör. print(), len(), +) etkileşimini tanımlar.
  • “Sihirli” metotlar olarak da bilinir.
  • Nesnelerinizi daha “Pythonic” ve kullanışlı hale getirir.

Örnek:

class MagicDemo:
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return f"Değer: {self.value}"

obj = MagicDemo(42)
print(obj)  # Çıktı: Değer: 42

Temel Özel Metotlar

__str__:

Kullanıcı dostu string temsili ( print(), str() )

__repr__:

Geliştirici dostu, nesneyi yeniden oluşturabilecek temsili ( repr() )

Örnek 1: Öğrenci

class Ogrenci:
    def __init__(self, ad, soyad, no):
        self.ad = ad
        self.soyad = soyad
        self.no = no

    def __str__(self):
        return f"{self.ad} {self.soyad} ({self.no})"

    def __repr__(self):
        return f"Ogrenci('{self.ad}', '{self.soyad}', {self.no})"

ogr = Ogrenci("Ali", "Yılmaz", 12345)
print(ogr)         # Çıktı: Ali Yılmaz (12345)
print(repr(ogr))   # Çıktı: Ogrenci('Ali', 'Yılmaz', 12345)

__len__

  • len() fonksiyonu ile nesnenin uzunluğunu döndürür.
  • Koleksiyon benzeri sınıflar için idealdir

Örnek 1: Kitap Listesi

class KitapListesi:
    def __init__(self):
        self.kitaplar = []

    def ekle(self, kitap):
        self.kitaplar.append(kitap)

    def __len__(self):
        return len(self.kitaplar)

liste = KitapListesi()
liste.ekle("Python 101")
liste.ekle("Veri Bilimi")
print(len(liste))  # Çıktı: 2

__getitem__:

İndeksleme veya anahtar erişimi ( obj[key] )

__setitem__:

İndeks/anahtar ile değer atama ( obj[key] = value )

Örnek 1: Sınıf Listesi

class SinifListesi:
    def __init__(self):
        self.ogrenciler = {}

    def __getitem__(self, key):
        return self.ogrenciler.get(key, "Öğrenci yok")

    def __setitem__(self, key, value):
        self.ogrenciler[key] = value

liste = SinifListesi()
liste[123] = "Ayşe Yılmaz"
print(liste[123])  # Çıktı: Ayşe Yılmaz
print(liste[999])  # Çıktı: Öğrenci yok

Örnek 2: Ürün Listesi

class UrunListesi:
    def __init__(self):
        self.urunler = {}

    def __getitem__(self, key):
        return self.urunler.get(key, None)

    def __setitem__(self, key, value):
        self.urunler[key] = value

ulist = UrunListesi()
ulist["elma"] = 10
print(ulist["elma"])  # Çıktı: 10
print(ulist["muz"])   # Çıktı: None

__delitem__

İndeks/anahtar ile eleman silme ( del obj[key] )

Örnek: Kitap İçeriği

class Kitap:
    def __init__(self, baslik):
        self.baslik = baslik
        self.icerik = {}

    def __setitem__(self, sayfa, metin):
        self.icerik[sayfa] = metin

    def __getitem__(self, sayfa):
        return self.icerik.get(sayfa, "Boş sayfa")

    def __delitem__(self, sayfa):
        del self.icerik[sayfa]

kitap = Kitap("Python")
kitap[1] = "Giriş"
print(kitap[1])  # Çıktı: Giriş
del kitap[1]
print(kitap[1])  # Çıktı: Boş sayfa

Operatör Aşırı Yükleme (Operator Overloading) Nedir?

  • Operatörlerin ( +, -, *, vb.) sınıflar için özel anlamlar kazanmasını sağlar.
  • Matematiksel veya mantıksal işlemleri doğal hale getirir

Temel Aritmetik Operatörler

Operatör Metot İşlev
+ __add__ Toplama
- __sub__ Çıkarma
* __mul__ Çarpma
/ __truediv__ Bölme
// __floordiv__ Tam Bölme
% __mod__ Mod Alma
** __pow__ Üs Alma

Aritmetik Operatörler: Örnek 1 - Vektör (1/2)

class Vektor:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        return Vektor(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        return Vektor(self.x - other.x, self.y - other.y)

    def __str__(self):
        return f"({self.x}, {self.y})"

Aritmetik Operatörler: Örnek 1 - Vektör (2/2)

v1 = Vektor(2, 3)
v2 = Vektor(4, 5)
print(v1 + v2)  # Çıktı: (6, 8)
print(v1 - v2)  # Çıktı: (-2, -2)

Aritmetik Operatörler: Örnek 2 - Para (1/2)

class Para:
    def __init__(self, miktar):
        self.miktar = miktar

    def __add__(self, other):
        if isinstance(other, Para):
            return Para(self.miktar + other.miktar)
        return Para(self.miktar + other)

    def __mul__(self, katsayi):
        return Para(self.miktar * katsayi)

    def __str__(self):
        return f"{self.miktar:.2f} TL"

Aritmetik Operatörler: Örnek 2 - Para (2/2)

p1 = Para(50)
p2 = Para(25)
print(p1 + p2)  # Çıktı: 75.00 TL
print(p1 * 2)   # Çıktı: 100.00 TL

isinstance()

  • Python’da bir nesnenin belirli bir sınıfa veya veri tipine ait olup olmadığını kontrol etmek için kullanılır.
  • type() fonksiyonuna göre daha esnektir (kalıtımı dikkate alır).
  • İki argüman alır:
    1. object: Tipi kontrol edilecek nesne.
    2. classinfo: Sınıf, tip veya bunların bir demeti (tuple).
  • Sonuç:
    • True: Nesne, belirtilen sınıf(lar)dan veya tip(ler)den birine aitse.
    • False: Aksi takdirde.

Neden type() Yerine isinstance()?

  • Kalıtım (Inheritance): isinstance(), alt sınıfları da doğru şekilde tanır. type() ise yalnızca nesnenin doğrudan oluşturulduğu sınıfı kontrol eder.
  • Esneklik: Birden fazla sınıf veya tip aynı anda kontrol edilebilir.

Temel Kullanım Örneği

sayi = 10
print(isinstance(sayi, int))  # Çıktı: True
print(isinstance(sayi, float)) # Çıktı: False
print(isinstance(sayi, (int, float, str)))  # Çıktı: True (çoklu kontrol)

metin = "Merhaba"
print(isinstance(metin, str))  # Çıktı: True
print(isinstance(metin, object)) # Çıktı: True (her şey object'ten türer)

liste = [1, 2, 3]
print(isinstance(liste, list)) # Çıktı: True

Kalıtım ile Kullanım Örneği

class Hayvan:
    pass

class Kopek(Hayvan):
    pass

class Kedi(Hayvan):
    pass

karabas = Kopek()
print(isinstance(karabas, Kopek))   # Çıktı: True
print(isinstance(karabas, Hayvan))  # Çıktı: True  (Kalıtımı dikkate alır!)
print(type(karabas) == Hayvan)       # Çıktı: False (type() kalıtımı dikkate almaz)
print(isinstance(karabas, Kedi)) # Çıktı: False

mirmir = Kedi()
print(isinstance(mirmir, (Kopek, Kedi))) # Çıktı: True (çoklu kontrol)

Karşılaştırma Operatörleri

Operatör Metot İşlev
== __eq__ Eşitlik
!= __ne__ Eşit Değil
< __lt__ Küçük
<= __le__ Küçük veya Eşit
> __gt__ Büyük
>= __ge__ Büyük veya Eşit

Örnek: Öğrenci Ortalama

class Ogrenci:
    def __init__(self, ad, ortalama):
        self.ad = ad
        self.ortalama = ortalama

    def __eq__(self, other):
        return self.ortalama == other.ortalama

    def __lt__(self, other):
        return self.ortalama < other.ortalama

ogr1 = Ogrenci("Ali", 85)
ogr2 = Ogrenci("Ayşe", 90)
print(ogr1 == ogr2)  # Çıktı: False
print(ogr1 < ogr2)   # Çıktı: True

Ek Özel Metotlar

__contains__

in operatörü ile eleman varlığını kontrol eder

Örnek: Kurs

class Kurs:
    def __init__(self, ad, ogrenciler=None):
        self.ad = ad
        self.ogrenciler = ogrenciler or []

    def __contains__(self, ogrenci):
        return ogrenci in self.ogrenciler

kurs = Kurs("Python", ["Ali", "Ayşe"])
print("Ali" in kurs)    # Çıktı: True
print("Mehmet" in kurs) # Çıktı: False

__call__

Nesneyi fonksiyon gibi çağırılabilir yapar

Örnek: Kişi

class Kisi:
    def __init__(self, isim):
        self.isim = isim

    def __call__(self, mesaj):
        return f"{self.isim}: {mesaj}"

kisi = Kisi("Ahmet")
print(kisi("Merhaba!"))  # Çıktı: Ahmet: Merhaba!

__iter__ ve __next__

Bu metotlar nesneyi döngüye uygun hale getirir.

Örnek: Kitap (Bölüm 1)

class Kitap:
    def __init__(self, ad, bolumler):
        self.ad = ad
        self.bolumler = bolumler

    def __iter__(self):
        self._index = 0
        return self

Örnek: Kitap (Bölüm 2)

    def __next__(self):
        if self._index >= len(self.bolumler):
            raise StopIteration
        sayfa = self.bolumler[self._index]
        self._index += 1
        return sayfa

kitap = Kitap("Python", ["Giriş", "Temeller", "Sonuç"])
for sayfa in kitap:
    print(sayfa)
# Çıktı: Giriş, Temeller, Sonuç

__enter__ ve __exit__

with bloğu ile kaynak yönetimi sağlar

Örnek: Dosya Yöneticisi

class DosyaYonetici:
    def __init__(self, dosya_adi, mod):
        self.dosya_adi = dosya_adi
        self.mod = mod

    def __enter__(self):
        self.dosya = open(self.dosya_adi, self.mod)
        return self.dosya

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.dosya.close()

with DosyaYonetici("test.txt", "w") as f:
    f.write("Merhaba!")

Gerçek Dünya Örneği: İstatistikli Liste

class IstatistikliListe:
    def __init__(self, liste=None):
        self.liste = liste or []
        self.erisimler = {}

    def __getitem__(self, indeks):
        self.erisimler[indeks] = self.erisimler.get(indeks, 0) + 1
        return self.liste[indeks]

    def __str__(self):
        return str(self.liste)

    def erisim_sayilari(self):
        return self.erisimler

lst = IstatistikliListe([10, 20, 30])
print(lst[0])  # Çıktı: 10
print(lst[0])  # Çıktı: 10
print(lst.erisim_sayilari())  # Çıktı: {0: 2}

Özet

  • Özel metotlar, sınıflara Python’un yerleşik davranışlarını kazandırır
  • Operatör aşırı yükleme, nesneler üzerinde doğal işlemler sağlar
  • Örnekler:
    • __str__, __repr__: Temsil
    • __len__, __getitem__: Koleksiyon
    • __add__, __eq__: Operatörler
  • Kodunuzu daha okunabilir ve esnek hale getirir

Teşekkürler!

class Tesekkur:
    def __str__(self):
        return "Dersi dinlediğiniz için teşekkürler!"

print(Tesekkur())