Decorator Pattern
Bir fonksiyonun çalışma süresini ölçmek istiyorsun. Her fonksiyona başlangıç/bitiş zamanı kodu mu ekleyeceksin? Ya da her fonksiyona log mu yazacaksın? Peki ya aynı "ek özelliği" 50 farklı fonksiyona eklemen gerekirse?
İşte decorator'lar tam da bu sorunu çözer. Fonksiyonun kendisine dokunmadan, ona yeni özellikler eklemeni sağlar.
Bu derste decorator'ların nasıl çalıştığını sıfırdan anlayacak, kendi decorator'larını yazacak ve gerçek dünyada kullanılan pattern'ları öğreneceksin.
Decorator Nedir?
Hediye paketi analojisi: Bir hediye düşün. Hediyenin kendisi (fonksiyon) değişmiyor — ama onu güzel bir kağıda sarıyorsun (decorator). Dışarıdan bakınca paketli bir hediye görüyorsun. Paketi açınca içindeki hediye aynen duruyor.
Teknik olarak: Decorator, bir fonksiyonu parametre olarak alan ve yeni bir fonksiyon döndüren fonksiyondur. Bu yeni fonksiyon, orijinal fonksiyonu sarar (wrap) ve onun öncesine/sonrasına ek işlemler ekler.
Temel: Fonksiyonlar First-Class Object
Decorator'ları anlamak için önce fonksiyonların Python'da nasıl davrandığını hatırlayalım.
Fonksiyon Parametre Olarak Geçilebilir
def selamla(isim):
return f"Merhaba, {isim}!"
def cagir_ve_yazdir(fonksiyon, deger):
"""Verilen fonksiyonu verilen değerle çağırır ve yazdırır."""
sonuc = fonksiyon(deger)
print(sonuc)
# Fonksiyonu parametre olarak geçiyoruz
cagir_ve_yazdir(selamla, "Ahmet")
# Merhaba, Ahmet!Fonksiyon Başka Fonksiyondan Döndürülebilir
def selamlama_sec(resmi=False):
"""Duruma göre farklı selamlama fonksiyonu döndürür."""
def resmi_selam(isim):
return f"Sayın {isim}, hoş geldiniz."
def samimi_selam(isim):
return f"Selam {isim}, naber!"
if resmi:
return resmi_selam
return samimi_selam
# Fonksiyon döndürülüyor
selam = selamlama_sec(resmi=True)
print(selam("Müdür Bey")) # Sayın Müdür Bey, hoş geldiniz.
selam = selamlama_sec(resmi=False)
print(selam("Ali")) # Selam Ali, naber!Bu iki özellik decorator'ların temelidir. Bir fonksiyonu alıp, onu sarıp, yeni fonksiyonu döndürmek.
Wrapper Pattern: Fonksiyonu Sarmalama
Decorator'ın özü şu pattern'dır:
def benim_decorator(fonksiyon):
"""Bir fonksiyonu sarıp ek işlem ekler."""
def wrapper(*args, **kwargs):
# --- ÖNCESİNDE yapılacak işlemler ---
print(f"🔵 {fonksiyon.__name__} çağrılıyor...")
# Orijinal fonksiyonu çağır
sonuc = fonksiyon(*args, **kwargs)
# --- SONRASINDA yapılacak işlemler ---
print(f"🟢 {fonksiyon.__name__} tamamlandı.")
return sonuc
return wrapperBu decorator'ı nasıl kullanırız?
def benim_decorator(fonksiyon):
def wrapper(*args, **kwargs):
print(f"🔵 {fonksiyon.__name__} çağrılıyor...")
sonuc = fonksiyon(*args, **kwargs)
print(f"🟢 {fonksiyon.__name__} tamamlandı.")
return sonuc
return wrapper
def topla(a, b):
return a + b
# Decorator'ı manuel uygula
topla = benim_decorator(topla)
# Artık topla() çağrıldığında ek işlemler de çalışır
sonuc = topla(3, 5)
# 🔵 topla çağrılıyor...
# 🟢 topla tamamlandı.
print(sonuc) # 8Olan şey:
benim_decorator(topla)→wrapperfonksiyonunu döndürürtopla = wrapper→ artıktopladediğimizdewrapperçağrılırwrapperiçinde orijinaltopla(closure ile hatırlanan) çağrılır
@ Syntax Sugar
Her fonksiyona fonksiyon = decorator(fonksiyon) yazmak zahmetli. Python bize @ söz dizimini sunar:
def benim_decorator(fonksiyon):
def wrapper(*args, **kwargs):
print(f"🔵 {fonksiyon.__name__} çağrılıyor...")
sonuc = fonksiyon(*args, **kwargs)
print(f"🟢 {fonksiyon.__name__} tamamlandı.")
return sonuc
return wrapper
@benim_decorator # ← Bu satır: topla = benim_decorator(topla)
def topla(a, b):
"""İki sayıyı toplar."""
return a + b
@benim_decorator # ← Bu satır: carp = benim_decorator(carp)
def carp(a, b):
"""İki sayıyı çarpar."""
return a * b
print(topla(3, 5)) # 8 (+ log çıktıları)
print(carp(4, 7)) # 28 (+ log çıktıları)@decorator sadece syntactic sugar — yani güzel görünen bir kısayol. Arka planda aynı şey oluyor.
functools.wraps: Metadata Koruma
Decorator uyguladığında bir sorun var: orijinal fonksiyonun adı ve docstring'i kaybolur.
def benim_decorator(fonksiyon):
def wrapper(*args, **kwargs):
return fonksiyon(*args, **kwargs)
return wrapper
@benim_decorator
def topla(a, b):
"""İki sayıyı toplar."""
return a + b
print(topla.__name__) # wrapper ← "topla" değil!
print(topla.__doc__) # None ← Docstring kayboldu!
help(topla) # wrapper(*args, **kwargs) ← Yanlış bilgi!Çünkü topla artık wrapper fonksiyonuna işaret ediyor. Bunu düzeltmek için functools.wraps kullanırız:
from functools import wraps
def benim_decorator(fonksiyon):
@wraps(fonksiyon) # ← Bu satır metadata'yı korur
def wrapper(*args, **kwargs):
return fonksiyon(*args, **kwargs)
return wrapper
@benim_decorator
def topla(a, b):
"""İki sayıyı toplar."""
return a + b
print(topla.__name__) # topla ✅
print(topla.__doc__) # İki sayıyı toplar. ✅⚠️ Dikkat: Her decorator'da
@functools.wraps(fonksiyon)kullan. Bu bir zorunluluk değil ama kullanmamak debugging ve dokümantasyon sorunlarına yol açar. Profesyonel koddawrapsolmazsa code review'dan geçmez.
Pratik Decorator Örnekleri
1. @timer — Çalışma Süresini Ölç
import time
from functools import wraps
def timer(fonksiyon):
"""Fonksiyonun çalışma süresini ölçer."""
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
baslangic = time.perf_counter()
sonuc = fonksiyon(*args, **kwargs)
bitis = time.perf_counter()
sure = bitis - baslangic
print(f"⏱️ {fonksiyon.__name__} → {sure:.4f} saniye")
return sonuc
return wrapper
@timer
def agir_islem(n):
"""N'e kadar olan sayıların toplamını hesaplar."""
return sum(range(n))
@timer
def bekle(saniye):
"""Belirli süre bekler."""
time.sleep(saniye)
sonuc = agir_islem(10_000_000)
# ⏱️ agir_islem → 0.2341 saniye
bekle(1)
# ⏱️ bekle → 1.0012 saniyeBu decorator'ı projelerde performans ölçümü için kullanabilirsin. Fonksiyonun kendisine dokunmadın — sadece üzerine @timer yazdın.
2. @retry — Başarısız İşlemi Tekrar Dene
import time
from functools import wraps
def retry(max_deneme=3, bekleme=1):
"""Başarısız fonksiyonu belirli sayıda tekrar dener."""
def decorator(fonksiyon):
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
for deneme in range(1, max_deneme + 1):
try:
return fonksiyon(*args, **kwargs)
except Exception as e:
print(f"⚠️ Deneme {deneme}/{max_deneme} başarısız: {e}")
if deneme < max_deneme:
print(f" {bekleme}s bekleniyor...")
time.sleep(bekleme)
else:
print(f"❌ Tüm denemeler başarısız!")
raise
return wrapper
return decorator
@retry(max_deneme=3, bekleme=0.5)
def guvenilmez_api():
"""Bazen başarısız olan bir API çağrısı simülasyonu."""
import random
if random.random() < 0.7: # %70 ihtimalle başarısız
raise ConnectionError("Sunucu yanıt vermedi!")
return {"status": "ok", "data": [1, 2, 3]}
# Çağır — başarısız olursa otomatik tekrar dener
try:
sonuc = guvenilmez_api()
print(f"✅ Başarılı: {sonuc}")
except ConnectionError:
print("Tüm denemeler başarısız oldu.")3. @cache (Memoization) — Sonuçları Önbelleğe Al
from functools import wraps
def cache(fonksiyon):
"""Fonksiyon sonuçlarını önbelleğe alır (memoization)."""
onbellek = {}
@wraps(fonksiyon)
def wrapper(*args):
if args in onbellek:
print(f" 📦 Cache hit: {fonksiyon.__name__}{args}")
return onbellek[args]
print(f" 🔄 Hesaplanıyor: {fonksiyon.__name__}{args}")
sonuc = fonksiyon(*args)
onbellek[args] = sonuc
return sonuc
wrapper.cache_clear = lambda: onbellek.clear()
wrapper.cache_info = lambda: dict(size=len(onbellek), entries=list(onbellek.keys()))
return wrapper
@cache
def fibonacci(n):
"""N'inci Fibonacci sayısını hesaplar."""
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10))
# İlk çağrıda hesaplar, sonraki çağrılarda cache'den döner
# 55
print(fibonacci(8))
# 📦 Cache hit! Çünkü fibonacci(10) hesaplanırken fibonacci(8) de hesaplandı
# 21💡 İpucu: Python 3.9+ sürümlerde
functools.cachevefunctools.lru_cachebuilt-in olarak gelir. Kendi cache decorator'ını yazmana gerek yok:
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(100)) # Anında hesaplar
print(fibonacci.cache_info())
# CacheInfo(hits=98, misses=101, maxsize=128, currsize=101)4. @validate — Girdi Doğrulama
from functools import wraps
def validate_types(**tip_bilgileri):
"""Parametre tiplerini kontrol eden decorator."""
def decorator(fonksiyon):
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
# Positional argümanları kontrol et
param_isimleri = fonksiyon.__code__.co_varnames
for i, (deger, isim) in enumerate(zip(args, param_isimleri)):
if isim in tip_bilgileri:
beklenen_tip = tip_bilgileri[isim]
if not isinstance(deger, beklenen_tip):
raise TypeError(
f"'{isim}' parametresi {beklenen_tip.__name__} olmalı, "
f"{type(deger).__name__} verildi!"
)
# Keyword argümanları kontrol et
for isim, deger in kwargs.items():
if isim in tip_bilgileri:
beklenen_tip = tip_bilgileri[isim]
if not isinstance(deger, beklenen_tip):
raise TypeError(
f"'{isim}' parametresi {beklenen_tip.__name__} olmalı, "
f"{type(deger).__name__} verildi!"
)
return fonksiyon(*args, **kwargs)
return wrapper
return decorator
@validate_types(ad=str, yas=int, maas=(int, float))
def calisanOlustur(ad, yas, maas):
return {"ad": ad, "yas": yas, "maas": maas}
print(calisanOlustur("Ahmet", 30, 15000.0))
# {'ad': 'Ahmet', 'yas': 30, 'maas': 15000.0} ✅
# calisanOlustur(123, 30, 15000)
# TypeError: 'ad' parametresi str olmalı, int verildi!Parametreli Decorator
Şimdiye kadar gördüğümüz @timer parametresizdi. Ama @retry(max_deneme=3) gibi parametreli decorator nasıl yazılır?
Anahtar: üç katmanlı fonksiyon.
from functools import wraps
def tekrarla(kac_kere):
"""Fonksiyonu belirtilen sayıda tekrar çalıştıran decorator."""
def decorator(fonksiyon):
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
sonuc = None
for i in range(kac_kere):
print(f" Çalıştırma {i+1}/{kac_kere}")
sonuc = fonksiyon(*args, **kwargs)
return sonuc
return wrapper
return decorator
@tekrarla(kac_kere=3) # tekrarla(3) → decorator döndürür → decorator(selamla) → wrapper
def selamla(isim):
print(f"Merhaba, {isim}!")
selamla("Ali")Çıktı:
Çalıştırma 1/3
Merhaba, Ali!
Çalıştırma 2/3
Merhaba, Ali!
Çalıştırma 3/3
Merhaba, Ali!Katmanlar:
tekrarla(kac_kere=3)→decoratorfonksiyonunu döndürürdecorator(selamla)→wrapperfonksiyonunu döndürürwrapper("Ali")→ asıl işi yapar
Hem Parametreli Hem Parametresiz Çalışan Decorator
Bazen @decorator ve @decorator(param) her ikisini de desteklemek istersin:
from functools import wraps
def log(fonksiyon=None, *, seviye="INFO"):
"""Hem @log hem @log(seviye="DEBUG") olarak kullanılabilir."""
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
print(f"[{seviye}] {fn.__name__} çağrılıyor")
return fn(*args, **kwargs)
return wrapper
if fonksiyon is not None:
# @log şeklinde kullanıldı (parametresiz)
return decorator(fonksiyon)
# @log(seviye="DEBUG") şeklinde kullanıldı
return decorator
@log
def fonksiyon_a():
print("A çalıştı")
@log(seviye="DEBUG")
def fonksiyon_b():
print("B çalıştı")
fonksiyon_a()
# [INFO] fonksiyon_a çağrılıyor
# A çalıştı
fonksiyon_b()
# [DEBUG] fonksiyon_b çağrılıyor
# B çalıştıÇoklu Decorator: Stacking Sırası
Bir fonksiyona birden fazla decorator uygulayabilirsin:
from functools import wraps
def kalin(fonksiyon):
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
return f"<b>{fonksiyon(*args, **kwargs)}</b>"
return wrapper
def italik(fonksiyon):
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
return f"<i>{fonksiyon(*args, **kwargs)}</i>"
return wrapper
def alti_cizili(fonksiyon):
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
return f"<u>{fonksiyon(*args, **kwargs)}</u>"
return wrapper
@kalin
@italik
@alti_cizili
def mesaj():
return "Merhaba Dünya"
print(mesaj())
# <b><i><u>Merhaba Dünya</u></i></b>Sıralama aşağıdan yukarı uygulanır:
Önce
@alti_cizili→mesaj = alti_cizili(mesaj)Sonra
@italik→mesaj = italik(mesaj)Son olarak
@kalin→mesaj = kalin(mesaj)
Yani @kalin @italik @alti_cizili mesaj aslında kalin(italik(alti_cizili(mesaj))) demek.
Daha gerçekçi bir örnek:
from functools import wraps
import time
def timer(fonksiyon):
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
start = time.perf_counter()
sonuc = fonksiyon(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"⏱️ {fonksiyon.__name__}: {elapsed:.4f}s")
return sonuc
return wrapper
def log(fonksiyon):
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
print(f"📝 {fonksiyon.__name__} çağrıldı: args={args}, kwargs={kwargs}")
sonuc = fonksiyon(*args, **kwargs)
print(f"📝 {fonksiyon.__name__} döndü: {sonuc}")
return sonuc
return wrapper
@timer # 2. sırada uygulanır (dışta)
@log # 1. sırada uygulanır (içte)
def hesapla(n):
return sum(range(n))
hesapla(1000000)
# 📝 hesapla çağrıldı: args=(1000000,), kwargs={}
# 📝 hesapla döndü: 499999500000
# ⏱️ hesapla: 0.0234s@timer dışta olduğu için @log'un süresini de dahil ediyor. Sıralamayı değiştirsen sonuç farklı olurdu.
Class-Based Decorator
Decorator'ı bir class olarak da yazabilirsin. __call__ metodu sayesinde class instance'ı fonksiyon gibi çağrılabilir:
from functools import wraps
class CountCalls:
"""Fonksiyonun kaç kere çağrıldığını sayar."""
def __init__(self, fonksiyon):
wraps(fonksiyon)(self) # metadata'yı koru
self.fonksiyon = fonksiyon
self.sayac = 0
def __call__(self, *args, **kwargs):
self.sayac += 1
print(f"📊 {self.fonksiyon.__name__} → {self.sayac}. çağrı")
return self.fonksiyon(*args, **kwargs)
@CountCalls
def selamla(isim):
return f"Merhaba, {isim}!"
print(selamla("Ali")) # 📊 selamla → 1. çağrı → Merhaba, Ali!
print(selamla("Veli")) # 📊 selamla → 2. çağrı → Merhaba, Veli!
print(selamla("Ayşe")) # 📊 selamla → 3. çağrı → Merhaba, Ayşe!
# Sayaca erişebilirsin
print(f"Toplam çağrı: {selamla.sayac}") # 3Class-based decorator'lar state tutmak gerektiğinde kullanışlı. Ama çoğu durumda fonksiyon-based decorator yeterli.
Parametreli Class-Based Decorator
class RateLimit:
"""Fonksiyonun belirli aralıklarla çağrılmasını zorlar."""
def __init__(self, min_aralik=1.0):
self.min_aralik = min_aralik
self.son_cagri = 0
def __call__(self, fonksiyon):
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
import time
simdi = time.time()
gecen = simdi - self.son_cagri
if gecen < self.min_aralik:
kalan = self.min_aralik - gecen
print(f"⏳ Rate limit! {kalan:.1f}s beklemelisiniz.")
return None
self.son_cagri = simdi
return fonksiyon(*args, **kwargs)
return wrapper
@RateLimit(min_aralik=2.0)
def api_cagri():
return "Veri alındı!"
print(api_cagri()) # Veri alındı!
print(api_cagri()) # ⏳ Rate limit! 1.8s beklemelisiniz. → NoneBuilt-in Decorator'lar
Python birçok yerleşik decorator sunar. Bunlardan en önemli üçünü kısaca hatırlayalım:
@property — Attribute Gibi Erişim
class Daire:
def __init__(self, yaricap):
self._yaricap = yaricap
@property
def yaricap(self):
"""Yarıçapı döndürür."""
return self._yaricap
@yaricap.setter
def yaricap(self, deger):
if deger < 0:
raise ValueError("Yarıçap negatif olamaz!")
self._yaricap = deger
@property
def alan(self):
"""Alanı hesaplar (salt okunur)."""
return 3.14159 * self._yaricap ** 2
d = Daire(5)
print(d.yaricap) # 5 (getter — fonksiyon çağrısı gibi görünmüyor!)
print(d.alan) # 78.53975
d.yaricap = 10 # setter
print(d.alan) # 314.159
# d.alan = 100 # AttributeError: setter yok, salt okunur
# d.yaricap = -5 # ValueError: Yarıçap negatif olamaz!@staticmethod — Instance Gerektirmeyen Metod
class Matematik:
@staticmethod
def topla(a, b):
return a + b
@staticmethod
def faktoriyel(n):
sonuc = 1
for i in range(2, n + 1):
sonuc *= i
return sonuc
# Instance oluşturmadan çağırabilirsin
print(Matematik.topla(3, 5)) # 8
print(Matematik.faktoriyel(5)) # 120@classmethod — Class'a Bağlı Metod
class Tarih:
def __init__(self, gun, ay, yil):
self.gun = gun
self.ay = ay
self.yil = yil
@classmethod
def from_string(cls, tarih_str):
"""String'den Tarih oluşturur (alternatif constructor)."""
gun, ay, yil = map(int, tarih_str.split("-"))
return cls(gun, ay, yil)
@classmethod
def bugun(cls):
"""Bugünün tarihini döndürür."""
from datetime import date
today = date.today()
return cls(today.day, today.month, today.year)
def __repr__(self):
return f"{self.gun:02d}/{self.ay:02d}/{self.yil}"
t1 = Tarih(15, 1, 2024)
t2 = Tarih.from_string("20-06-2024")
t3 = Tarih.bugun()
print(t1) # 15/01/2024
print(t2) # 20/06/2024
print(t3) # bugünün tarihiGerçek Dünya Decorator Pattern'ları
Authentication Decorator (Web Framework Tarzı)
from functools import wraps
# Basit kullanıcı veritabanı
aktif_oturumlar = {"abc123": {"ad": "Ahmet", "rol": "admin"}}
def login_required(fonksiyon):
"""Kullanıcının giriş yapmış olmasını zorunlu kılar."""
@wraps(fonksiyon)
def wrapper(token, *args, **kwargs):
if token not in aktif_oturumlar:
print("❌ Yetkisiz erişim! Lütfen giriş yapın.")
return None
kullanici = aktif_oturumlar[token]
print(f"✅ Hoş geldin, {kullanici['ad']}!")
return fonksiyon(token, *args, **kwargs)
return wrapper
def admin_required(fonksiyon):
"""Admin yetkisi gerektirir."""
@wraps(fonksiyon)
def wrapper(token, *args, **kwargs):
kullanici = aktif_oturumlar.get(token)
if not kullanici or kullanici["rol"] != "admin":
print("❌ Bu işlem için admin yetkisi gerekli!")
return None
return fonksiyon(token, *args, **kwargs)
return wrapper
@login_required
def profil_goster(token):
kullanici = aktif_oturumlar[token]
print(f"Profil: {kullanici}")
@login_required
@admin_required
def kullanici_sil(token, hedef_id):
print(f"Kullanıcı #{hedef_id} silindi.")
# Test
profil_goster("abc123") # ✅ Hoş geldin, Ahmet! → Profil bilgisi
profil_goster("yanlis_token") # ❌ Yetkisiz erişim!
kullanici_sil("abc123", 42) # ✅ Admin → Kullanıcı #42 silindi.Deprecation Warning Decorator
import warnings
from functools import wraps
def deprecated(yeni_fonksiyon=None):
"""Fonksiyonun kullanımdan kaldırıldığını bildirir."""
def decorator(fonksiyon):
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
mesaj = f"⚠️ {fonksiyon.__name__}() kullanımdan kaldırıldı."
if yeni_fonksiyon:
mesaj += f" Bunun yerine {yeni_fonksiyon}() kullanın."
warnings.warn(mesaj, DeprecationWarning, stacklevel=2)
return fonksiyon(*args, **kwargs)
return wrapper
return decorator
@deprecated(yeni_fonksiyon="topla_yeni")
def topla_eski(a, b):
return a + b
def topla_yeni(a, b):
"""Yeni ve geliştirilmiş toplama fonksiyonu."""
return a + b
# Eski fonksiyon hâlâ çalışır ama uyarı verir
sonuc = topla_eski(3, 5) # DeprecationWarning uyarısı gösterirSingleton Decorator
from functools import wraps
def singleton(cls):
"""Bir class'ın sadece tek bir instance'ı olmasını sağlar."""
instances = {}
@wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Database:
def __init__(self):
print("🔌 Veritabanı bağlantısı oluşturuldu!")
self.connected = True
db1 = Database() # 🔌 Veritabanı bağlantısı oluşturuldu!
db2 = Database() # Sessiz — yeni instance oluşturmuyor!
print(db1 is db2) # True — aynı nesne!Decorator Yazarken Dikkat Edilecekler
1. Her Zaman *args, **kwargs Kullan
# ❌ Kötü: sadece belirli parametrelere izin verir
def kotu_decorator(fonksiyon):
def wrapper(a, b): # Sadece 2 parametreli fonksiyonlarda çalışır!
return fonksiyon(a, b)
return wrapper
# ✅ İyi: her fonksiyona uyar
def iyi_decorator(fonksiyon):
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
return fonksiyon(*args, **kwargs)
return wrapper2. Her Zaman @wraps Kullan
from functools import wraps
def decorator(fonksiyon):
@wraps(fonksiyon) # ← Bu satırı ASLA unutma
def wrapper(*args, **kwargs):
return fonksiyon(*args, **kwargs)
return wrapper3. Return Değerini Unutma
# ❌ Kötü: return'ü unuttuk!
def kotu(fonksiyon):
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
fonksiyon(*args, **kwargs) # Return yok!
return wrapper
@kotu
def topla(a, b):
return a + b
print(topla(3, 5)) # None! 😱 Return kayboldu!
# ✅ İyi: return'ü geçir
def iyi(fonksiyon):
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
return fonksiyon(*args, **kwargs) # Return'ü geçir!
return wrapper💡 İpucu: Decorator yazarken şu şablonu kullan ve her seferinde buradan başla:
from functools import wraps
def decorator_adi(fonksiyon):
"""Decorator'ın ne yaptığını açıkla."""
@wraps(fonksiyon)
def wrapper(*args, **kwargs):
# ÖNCESİNDE: ek işlemler
sonuc = fonksiyon(*args, **kwargs)
# SONRASINDA: ek işlemler
return sonuc
return wrapperDecorator Akış Diyagramı
@my_decorator
def my_function():
...
↓ Eşdeğeri:
my_function = my_decorator(my_function)
↓ my_decorator ne yapar?
def my_decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
# 1. Ön işlem (log, auth, timer start...)
result = fn(*args, **kwargs) # 2. Orijinal fonksiyon
# 3. Son işlem (log, timer stop...)
return result # 4. Sonucu döndür
return wrapper # 5. wrapper'ı döndür
↓ my_function("hello") çağrıldığında:
→ Aslında wrapper("hello") çalışır
→ wrapper içinde orijinal fn("hello") çağrılır
→ Ek işlemler + orijinal sonuç dönerÖzet
Decorator, bir fonksiyonu parametre olarak alıp onu saran yeni bir fonksiyon döndüren yapıdır.
@decoratorsöz dizimifonksiyon = decorator(fonksiyon)ile eşdeğerdir.Wrapper pattern:
def wrapper(*args, **kwargs)ile her türlü fonksiyonu sarmalayabilirsin. Orijinal fonksiyonu içeride çağırıp öncesine/sonrasına ek işlemler eklersin.`functools.wraps` orijinal fonksiyonun
__name__,__doc__gibi metadata'sını korur. Her decorator'da kullanmak zorunlu değil ama şiddetle tavsiye edilir.Parametreli decorator üç katmanlı fonksiyon gerektirir:
dış(param)→decorator(fn)→wrapper(*args, **kwargs).Çoklu decorator aşağıdan yukarıya uygulanır:
@A @B @C def f()→f = A(B(C(f))).Pratik decorator'lar:
@timer(performans),@retry(hata toleransı),@cache(memoization),@validate(girdi doğrulama),@login_required(yetkilendirme). Built-in'ler:@property,@staticmethod,@classmethod.
AI Asistan
Sorularını yanıtlamaya hazır