Nesne Tabanlı Programlama 2

Dönem Sonu Özeti

Emre Can Yılmaz

Ondokuz Mayıs Üniversitesi

2026

Dönem Sonu Tekrarının Amacı

Bu sunumda konuları en baştan yeniden anlatmayacağız.

Amaç:

  • Ana kavramları kısa kısa hatırlamak,
  • Kavramların birbirine nasıl bağlandığını görmek,
  • Kod okurken dikkat edilecek noktaları toparlamak,
  • Sık karıştırılan yerleri netleştirmek.

Dönemin Genel Haritası

Bu dönem şu çizgide ilerledik:

  1. Sınıf, nesne, self, __init__
  2. Sınıf / örnek nitelikleri ve metot türleri
  3. Kalıtım, super(), override
  4. Kapsülleme, @property, soyutlama
  5. Kompozisyon, polimorfizm, duck typing
  6. Özel metotlar ve operatör aşırı yükleme
  7. Dekoratörler ve fonksiyonel araçlar
  8. Iterator, generator, type hints
  9. SOLID prensipleri
  10. Unit test, TDD, debugging
  11. Tasarım desenleri
  12. Threading, multiprocessing, asyncio

OOP’nin Temel Fikri

OOP’de amaç yalnızca sınıf yazmak değildir.

Asıl amaç:

  • Veriyi ve davranışı anlamlı bir yapıda toplamak,
  • Sorumlulukları doğru nesnelere dağıtmak,
  • Değişikliklerin etkisini sınırlamak,
  • Kodu okunabilir ve test edilebilir hâle getirmektir.

Sınıf, Nesne, Nitelik, Metot

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

    def bilgi_ver(self):
        return f"{self.ad} - {self.numara}"

Bu örnekte:

  • Ogrenci → Sınıf
  • Ogrenci("Ayşe", 123) → Nesne
  • ad, numara → Nitelik
  • bilgi_ver() → Metot

__init__ ve self

__init__, nesnenin ilk durumunu ayarlar.

self, metot içinde o anki nesneyi temsil eder.

class Ders:
    def __init__(self, kod, ad):
        self.kod = kod
        self.ad = ad
        self.ogrenciler = []

    def ogrenci_ekle(self, ogrenci):
        self.ogrenciler.append(ogrenci)

Şu çağrıda:

ders.ogrenci_ekle(ogrenci)

Python, ders nesnesini metoda otomatik olarak gönderir.

Sınıf Niteliği ve Örnek Niteliği

class Ogrenci:
    universite = "OMÜ"

    def __init__(self, ad):
        self.ad = ad
  • universite → Sınıf niteliği
  • ad → Örnek niteliği

Dikkat edilmesi gereken nokta:

Sınıf niteliği tüm nesneler tarafından paylaşılır. Liste gibi değiştirilebilir yapılar sınıf düzeyinde tutulursa nesneler aynı listeyi paylaşabilir.

Metot Türleri

class Ders:
    toplam_ders = 0

    def __init__(self, ad):
        self.ad = ad
        Ders.toplam_ders += 1

    @classmethod
    def ders_sayisi(cls):
        return cls.toplam_ders

    @staticmethod
    def gecer_not_mu(not_degeri):
        return not_degeri >= 60

Kısa ayrım:

  • self → Nesneye erişir.
  • cls → Sınıfa erişir.
  • Static metot → Yardımcı davranış sağlar.

Kalıtım: is-a İlişkisi

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

class Ogrenci(Kisi):
    def __init__(self, ad, numara):
        super().__init__(ad)
        self.numara = numara

Kalıtım için temel soru:

Öğrenci gerçekten bir kişi türü müdür?

Cevap evetse kalıtım düşünülebilir.

Override ve super()

Alt sınıf, üst sınıftaki metodu yeniden tanımlayabilir.

class Kisi:
    def bilgi_ver(self):
        return self.ad

class Ogrenci(Kisi):
    def bilgi_ver(self):
        return f"{super().bilgi_ver()} - {self.numara}"

Burada:

  • bilgi_ver() override edilmiştir.
  • super() ile üst sınıftaki davranış korunmuştur.

Kapsülleme ve @property

Kapsülleme, nesnenin iç durumunu kontrollü biçimde yönetmektir.

class Ders:
    def __init__(self, ad, kredi):
        self.ad = ad
        self.kredi = kredi  # setter çalışır

    @property
    def kredi(self):
        return self._kredi

    @kredi.setter
    def kredi(self, deger):
        if deger <= 0:
            raise ValueError("Kredi pozitif olmalıdır")
        self._kredi = deger

__init__ içinde self.kredi = kredi yazıldığında doğrudan _kredi atanmaz; setter metodu çalışır.

Soyutlama

Soyutlama, ayrıntıları gizleyip gerekli arayüzü öne çıkarır.

from abc import ABC, abstractmethod

class Raporlayici(ABC):
    @abstractmethod
    def rapor_uret(self, ders):
        pass

Bu yapı alt sınıfları rapor_uret() metodunu yazmaya zorlar.

Yani ortak bir kontrat tanımlar.

Kompozisyon: has-a İlişkisi

class NotKaydi:
    def __init__(self, ogrenci, puan):
        self.ogrenci = ogrenci
        self.puan = puan

class Ders:
    def __init__(self, ad):
        self.ad = ad
        self.notlar = []

    def not_ekle(self, kayit):
        self.notlar.append(kayit)

Temel ilişki:

Ders, not kayıtlarına sahiptir.

Kalıtım mı Kompozisyon mu?

Kısa karar sorusu:

  • is-a → Kalıtım
  • has-a → Kompozisyon

Örnek:

  • Ogrenci, Kisi türüdür → Kalıtım
  • Ders, NotKaydi içerir → Kompozisyon

Sırf kod tekrarını azaltmak için kalıtım kullanmak çoğu zaman iyi bir tercih değildir.

Polimorfizm

Polimorfizm, aynı çağrının farklı nesnelerde farklı davranabilmesidir.

class PDFRapor:
    def rapor_uret(self, ders):
        return f"{ders.ad} PDF raporu"

class HTMLRapor:
    def rapor_uret(self, ders):
        return f"{ders.ad} HTML raporu"

raporlayicilar = [PDFRapor(), HTMLRapor()]

for raporlayici in raporlayicilar:
    print(raporlayici.rapor_uret(ders))

Kod, nesnenin tam sınıfını sormaz.

Duck Typing

Python’da ortak davranış bazen ortak üst sınıftan daha önemlidir.

def raporu_kaydet(raporlayici, ders):
    icerik = raporlayici.rapor_uret(ders)
    print(icerik.upper())

Burada raporlayici nesnesinin hangi sınıftan geldiği değil, şu davranışı sağlayıp sağlamadığı önemlidir:

rapor_uret(ders) -> str

Yalnızca metot adının aynı olması yetmez; metodun beklenen şekilde kullanılabilmesi gerekir.

Özel Metotlar

Özel metotlar, kendi sınıflarımızı Python’ın yerleşik davranışlarıyla uyumlu hâle getirir.

class Ders:
    def __init__(self, kod, ad):
        self.kod = kod
        self.ad = ad
        self.ogrenciler = []

    def __str__(self):
        return f"{self.kod} - {self.ad}"

    def __len__(self):
        return len(self.ogrenciler)
  • print(ders)__str__
  • len(ders)__len__

__str__ ve __repr__

İkisi de nesnenin metinsel temsilini verir; amaçları farklıdır.

def __str__(self):
    return f"{self.kod} - {self.ad}"

def __repr__(self):
    return f"Ders('{self.kod}', '{self.ad}')"
  • __str__ → Kullanıcıya daha okunabilir çıktı
  • __repr__ → Geliştiriciye daha açıklayıcı çıktı

Dekoratörler

Dekoratör, bir fonksiyonun davranışını fonksiyonun içini değiştirmeden genişletir.

from functools import wraps

def logla(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("İşlem başladı")
        sonuc = func(*args, **kwargs)
        print("İşlem bitti")
        return sonuc
    return wrapper
@logla
def not_hesapla(vize, final):
    return vize * 0.4 + final * 0.6

Fonksiyonel Araçlar

Python’da fonksiyonlar da nesnedir.

Bu yüzden fonksiyonlar:

  • Değişkene atanabilir,
  • Parametre olarak gönderilebilir,
  • Başka fonksiyondan döndürülebilir.
ogrenciler = [
    ("Ayşe", 85),
    ("Mehmet", 62),
    ("Zeynep", 94),
]

sirali = sorted(ogrenciler, key=lambda x: x[1], reverse=True)

Kısa yazmak her zaman daha anlaşılır kod anlamına gelmez.

Iterator ve Iterable

Iterable, üzerinde dolaşılabilen yapıdır.

Iterator, sıradaki elemanı üreten nesnedir.

notlar = [70, 85, 90]

it = iter(notlar)

print(next(it))
print(next(it))

for döngüsü perde arkasında iter() ve next() mantığıyla çalışır.

Generator Neden Kullanılır?

Generator, değerleri ihtiyaç oldukça üretir.

def notlari_oku(dosya_yolu):
    with open(dosya_yolu, encoding="utf-8") as dosya:
        for satir in dosya:
            ad, puan = satir.strip().split(",")
            yield ad, int(puan)

Kullanım:

for ad, puan in notlari_oku("notlar.csv"):
    print(ad, puan)

Avantaj:

Tüm dosyayı baştan belleğe almak yerine satır satır işlem yapılır.

Type Hints

Type hints, kodun hangi türlerle çalıştığını görünür yapar.

def ortalama(notlar: list[int]) -> float:
    return sum(notlar) / len(notlar)

Önemli nokta:

Type hints, Python’da otomatik çalışma zamanı tür kontrolü yapmaz.

Daha çok:

  • Okunabilirlik,
  • Editör desteği,
  • Statik analiz

için kullanılır.

SOLID Kısa Tekrar

SOLID, tasarım problemlerini fark etmek için kullanılır.

  • Tek Sorumluluk Prensibi
    (Single Responsibility Principle)
    Bir sınıf çok fazla iş mi yapıyor?

  • Açık/Kapalı Prensibi
    (Open/Closed Principle)
    Yeni özellik eklemek için sürekli eski kodu mu değiştiriyoruz?

  • Liskov Yerine Geçme Prensibi
    (Liskov Substitution Principle)
    Alt sınıf, üst sınıfın yerine sorunsuz geçebiliyor mu?

SOLID Kısa Tekrar - Devam

  • Arayüz Ayrımı Prensibi
    (Interface Segregation Principle)
    Sınıflar kullanmadığı metotlara zorlanıyor mu?

  • Bağımlılık Tersine Çevirme Prensibi
    (Dependency Inversion Principle)
    Üst seviye kod somut sınıflara fazla mı bağlı?

Amaç prensip adlarını ezberlemek değil, tasarım problemlerini tanımaktır.

Tasarım Desenleri

Tasarım desenleri, sık karşılaşılan yazılım problemleri için kullanılan genel çözüm kalıplarıdır.

Bu dönem gördüklerimiz:

  • Singleton
  • Factory Method
  • Decorator
  • Observer
  • Strategy

Desenler hazır kopyalanacak kodlar değildir.

Asıl değerleri, problemi nasıl düşündürdükleridir.

Desenleri Problem Üzerinden Hatırlayalım

  • Tek nesne yeterli olmalı
    → Singleton

  • Nesne oluşturma kararı merkezi ve esnek olmalı
    → Factory Method

  • Nesneye çalışma zamanında ek davranış verilmeli
    → Decorator

  • Bir değişiklikten başka nesneler haberdar edilmeli
    → Observer

  • Algoritma çalışma zamanında değiştirilebilmeli
    → Strategy

Unit Test

Unit test, kodun küçük ve bağımsız parçalarını sınar.

def ortalama(vize, final):
    return vize * 0.4 + final * 0.6

def test_ortalama():
    assert ortalama(50, 80) == 68

pytest ile bu test doğrudan çalıştırılabilir.

Test yazarken yalnızca fonksiyonu çağırmak yetmez; beklenen sonucu açıkça kontrol etmek gerekir.

Mocking ve TDD

Mocking, dış bağımlılıkları test sırasında taklit etmektir.

Kullanım nedenleri:

  • Gerçek API’ye gitmemek,
  • Veritabanını testte kullanmamak,
  • Dosya sistemi etkisini azaltmak,
  • Hata durumlarını kontrollü üretmek.

TDD döngüsü:

  1. Red
  2. Green
  3. Refactor

Debugging

Debugging, hatayı yalnızca görmek değil, nedenini anlamaktır.

Araçlar:

  • print()
  • IDE debugger
  • breakpoint()
  • pdb

Temel akış:

  1. Hatalı davranışı gözle.
  2. Şüpheli bölgeyi daralt.
  3. Değerleri incele.
  4. Düzelt.
  5. Yeniden test et.

Threading, Multiprocessing, Asyncio

Üçü aynı şey değildir.

  • threading
    Bekleme içeren işleri aynı zaman aralığında yönetmek için kullanılır.

  • multiprocessing
    CPU yoğun işleri farklı process’lere bölmek için kullanılır.

  • asyncio
    Çok sayıda bekleyen görevi tek akış içinde yönetmek için kullanılır.

Temel soru:

Program daha çok bekliyor mu, yoksa daha çok hesaplama mı yapıyor?

Hangisini Ne Zaman Düşünürüz?

Durum Daha uygun yaklaşım
Dosya indirme, ağ isteği, bekleme ağırlıklı işler threading veya asyncio
CPU yoğun hesaplama multiprocessing
Çok sayıda bağlantıyı tek akışta yönetme asyncio
Paylaşılan veri thread’ler arasında değişiyor Lock gerekir

Önemli ayrım:

Bekleme ağırlıklı iş ile hesaplama ağırlıklı iş aynı problem değildir.

Sık Karıştırılanlar

  • Sınıf ve nesne aynı şey değildir.
  • __init__, nesnenin ilk durumunu ayarlar.
  • self elle gönderilmez; Python otomatik geçirir.
  • Kalıtım her benzerlik için kullanılmaz.
  • Kompozisyon, kalıtıma güçlü bir alternatiftir.
  • Duck typing yalnızca aynı metot adı değildir.
  • Type hints çalışma zamanı tür kontrolü değildir.
  • Python decorator ile Decorator deseni aynı şey değildir.
  • Threading ve multiprocessing aynı amaç için kullanılmaz.

Kod Okurken Sorulacak Sorular

Bir OOP kodu gördüğünüzde şunları sorun:

  1. Bu sınıfın temel sorumluluğu ne?
  2. Nitelikler nesneye mi, sınıfa mı ait?
  3. Metot self, cls veya hiçbirini kullanıyor mu?
  4. İlişki is-a mı, has-a mı?
  5. Davranış polimorfizmle sadeleşebilir mi?
  6. Nesnenin iç durumu kontrollü korunuyor mu?
  7. Test yazmak kolay mı?
  8. Değişiklik yapınca kaç yer etkileniyor?

Kapanış

Bu dönemin ana fikri şudur:

İyi OOP kodu, sınıf sayısının fazla olmasıyla değil; sorumlulukların doğru dağıtılmasıyla ortaya çıkar.

İyi tasarlanmış kod:

  • Okunabilir,
  • Değiştirilebilir,
  • Test edilebilir,
  • Gereksiz bağımlılıklardan uzak,
  • Davranışı açık biçimde temsil eden koddur.

Teşekkürler

Dönem boyunca gösterdiğiniz ilgi ve katılım için teşekkür ederim.

Başarılar dilerim.