Son gün! Python’un ileri seviye özelliklerini keşfedeceğiz. Bu konular, dili tam anlamıyla kavramanızı ve daha profesyonel yazılım geliştirmenizi sağlayacak.

1. Iteratorlar ve Generatorlar

Iterator Nedir?

Iterator, üzerinde dolaşılabilen (iterable) nesneleri sırasıyla döndüren bir yapıdır. Listeler, demetler gibi veri yapıları iterator olarak kullanılabilir.

numbers = [1, 2, 3, 4]
iterator = iter(numbers)

print(next(iterator))  # 1
print(next(iterator))  # 2

Generator Nedir?

Generator, iterator’ların özel bir türüdür ve yield ifadesi ile tanımlanır. Memory dostudur — tüm veriyi aynı anda bellekte tutmaz, gerektiğinde üretir.

def sayi_generator():
    for i in range(5):
        yield i

for sayi in sayi_generator():
    print(sayi)

Bu kodda yield ifadesi sayesinde her bir sayı ihtiyaç duyuldukça üretilir. Büyük veri setlerinde RAM tasarrufu sağlar.

2. Context Managers ve with

Context Manager, bir dosya veya kaynak işlemi sonrasında temizlemeyi garanti eder. with ifadesi bunu sağlar:

with open("dosya.txt", "w") as dosya:
    dosya.write("Merhaba, dünya!")
# Burada dosya otomatik kapanır — hata olsa bile

Olası kaynak sızıntıları önlenmiş olur. Dosya, veritabanı bağlantısı, ağ socket’leri gibi her kaynak için ideal.

3. Dekoratörler

Dekoratörler, Python’da fonksiyonları dinamik olarak değiştirmek için kullanılan yapılardır. Fonksiyonlara yeni davranışlar kazandırır.

Fonksiyon Dekoratörü

def dekorator(fonksiyon):
    def sarmalici(*args, **kwargs):
        print("Fonksiyon başlamadan önce")
        sonuc = fonksiyon(*args, **kwargs)
        print("Fonksiyon tamamlandı")
        return sonuc
    return sarmalici

@dekorator
def selamla():
    print("Merhaba!")

selamla()
# Çıktı:
# Fonksiyon başlamadan önce
# Merhaba!
# Fonksiyon tamamlandı

@dekorator ile fonksiyonun çalışma şekli değiştirildi — kaynak kodu tek bir satır ile genişletildi.

Pratik Dekoratör Örnekleri

4. İleri Seviye Hata Yönetimi

Önceki günlerde temel hata yönetimine değindik. Şimdi özel hata sınıflarıyla sistematik bir yapı kuralım:

class OzellestirilmisHata(Exception):
    """Özel iş kuralları ihlallerinde fırlatılır."""
    pass

def hata_firlatici():
    raise OzellestirilmisHata("Bu bir özel hatadır.")

try:
    hata_firlatici()
except OzellestirilmisHata as e:
    print(f"Hata yakalandı: {e}")

Bir projede iş alanına özgü hatalar tanımlayarak, hata yönetimi mantığını netleştirebilir ve dokümante edebilirsiniz.

5. İleri Seviye Veri Yapıları

Queue (Kuyruk) — FIFO

FIFO (First In, First Out) mantığıyla çalışır.

from collections import deque

kuyruk = deque()

kuyruk.append("A")
kuyruk.append("B")

print(kuyruk.popleft())  # A
print(kuyruk.popleft())  # B

Stack (Yığın) — LIFO

LIFO (Last In, First Out) mantığıyla çalışır.

yigin = []

yigin.append(10)
yigin.append(20)

print(yigin.pop())  # 20
print(yigin.pop())  # 10

Deque (Double-ended Queue)

Her iki uçtan ekleme/çıkarma yapılabilen yapıdır. collections.deque hem stack hem queue olarak kullanılabilir, O(1) performansla.

6. Metaclass

Metaclass, Python’da sınıfların nasıl tanımlandığını ve nasıl davranacağını kontrol eder. Sınıf yapısına düşük seviyede müdahale imkânı verir.

class MyMeta(type):
    def __new__(cls, name, bases, attrs):
        print(f"Yeni bir sınıf oluşturuluyor: {name}")
        return super().__new__(cls, name, bases, attrs)

class MyClass(metaclass=MyMeta):
    pass

# Çıktı: Yeni bir sınıf oluşturuluyor: MyClass

Ne zaman kullanılır? Genelde framework geliştirenler kullanır (Django ORM, SQLAlchemy gibi). Günlük kodda nadiren ihtiyaç olur ama ileri Python ekosistemini anlamak için bilmek önemli.

7. Mini Proje: Dosya Okuma Iterator’u

Şimdi öğrendiklerimizi birleştiren küçük bir proje yapalım. Bir dizindeki tüm dosyaları okuyup içeriğini sırayla döndüren bir custom iterator:

import os

class DosyaIterator:
    def __init__(self, klasor_yolu):
        self.klasor_yolu = klasor_yolu
        self.dosyalar = iter(os.listdir(klasor_yolu))

    def __iter__(self):
        return self

    def __next__(self):
        dosya_adi = next(self.dosyalar)
        tam_yol = os.path.join(self.klasor_yolu, dosya_adi)
        with open(tam_yol, 'r') as f:
            return f.read()

# Kullanım
dosya_iteratoru = DosyaIterator("/path/to/klasor")
for dosya_icerik in dosya_iteratoru:
    print(dosya_icerik)

Bu iterator, verilen klasördeki her dosyayı sırayla açıp içeriğini döndürür. __iter__ ve __next__ metodlarını uygulayarak Python’un iterator protokolüne dahil olduk.

🎉 Tebrikler!

5 günlük Python BootCamp’ini başarıyla tamamladın!

Bu 5 günlük yolculukta, Python’un temellerinden başlayarak ileri düzey konulara kadar geniş bir yelpazede bilgi sahibi oldun. Her gün bir adım daha derinleştik. Artık:

Sonraki Adımlar

Python öğrenme yolculuğu burada bitmiyor — devamı senin elinde. 🐍