7 - Python’ın İleri Seviye Özellikleri I: Dekoratörler ve Fonksiyonel Yaklaşımlar
2026
Geçen haftalarda sınıfların davranışlarını tasarladık; özel metotlarla Python’ın bazı davranışları nasıl beklediğini gördük.
Bu hafta odağı biraz değiştiriyoruz:
Bu kez sınıfları değil, fonksiyonları daha esnek kullanmayı konuşacağız.
Bugün iki temel soruya cevap arıyoruz:
Bu iki sorunun ana araçları şunlardır:
lambda, map, filter, reduce, sorted(key=...))Bu dersin sonunda şunları ayırt edebiliyor olmanızı hedefliyoruz:
@dekorator yazımının perde arkasında ne yaptığını*args, **kwargs ve dönüş değerinin neden kritik olduğunufunctools.wraps kullanımının neden iyi bir alışkanlık olduğunulambda, map, filter, reduce araçlarının ne zaman yararlı, ne zaman gereksiz olduğunuPython’da fonksiyonlar:
Bu fikir anlaşılmadan dekoratör mantığı tam oturmaz.
def selamla(isim):
return f"Merhaba, {isim}!"
def islemi_uygula(fonksiyon, deger):
return fonksiyon(deger)
print(islemi_uygula(selamla, "Ayşe"))Burada selamla, sadece çağrılan bir şey değil; başka bir fonksiyona aktarılabilen bir değerdir.
Dekoratör, en basit haliyle şunu yapar:
Kısaca:
Fonksiyonu değiştirmeden, etrafına yeni davranış ekleriz.
Zihinsel model:
çağrı -> wrapper -> orijinal fonksiyon -> wrapper -> sonuç
Aynı yardımcı davranışı birçok fonksiyona ayrı ayrı yazmak yerine ortaklaştırabiliriz.
Örneğin:
Bu yüzden dekoratörler çoğu zaman şu problemi çözer:
“Aynı yardımcı davranışı birçok fonksiyona nasıl uygularım?”
@ sözdizimine geçmeden önce mekanizmayı çıplak haliyle görelim.
Şimdilik parametresiz bir örnekle başlıyoruz:
log_decorator, merhaba_de fonksiyonunu aldı.wrapper adında yeni bir fonksiyon üretti.Yani şu ifade kritik:
Dekoratör, orijinal fonksiyonun yerine geçen yeni bir fonksiyon üretir.
@ Yazımı Ne Anlama Gelir?Bu yazım, temelde şu anlama gelir:
Yani @ özel bir sihir değil; daha okunabilir bir kısayoldur.
İlk denemelerde sık görülen hata şudur:
Bu yapı yalnızca parametresiz fonksiyonlarda çalışır.
Fonksiyon parametre alıyorsa dekoratör kırılır.
Bu yüzden çoğu genel amaçlı dekoratörde şu yapı gerekir:
*args**kwargsDekoratör yazarken sık yapılan hatalardan biri de şudur:
func(...) çağrılır,Bu durumda orijinal fonksiyon değer üretse bile dışarıya None gider.
Pratik kural:
Dekoratör, mümkünse orijinal fonksiyonun çağrılma mantığını ve dönüş davranışını bozmamalıdır.
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__} {end - start:.4f} saniye sürdü.")
return result
return wrapper
@timer
def yavas_toplam():
time.sleep(1)
return sum(range(1000))
print(yavas_toplam())wraps Kullanıyoruz?@wraps(func) kullanılmazsa sarmalanan fonksiyonun bazı kimlik bilgileri kaybolur.
Örneğin:
Bu yüzden iyi pratik şudur:
Genel amaçlı dekoratör yazıyorsanız, çoğu durumda functools.wraps kullanın.
wraps Etkisini Görmekdef plain_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@plain_decorator
def selam():
"""Basit selamlama fonksiyonu"""
return "Merhaba"
print(selam.__name__) # wrapper
print(selam.__doc__) # None@wraps(func) kullanıldığında bu bilgiler korunur.
Bazen dekoratörün kendisi de ayar almak ister.
Örnek soru:
“Bu dekoratör hangi etiketi kullansın?”
Bu durumda yapı üç katmanlı olur:
wrapperfrom functools import wraps
def announce(label):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[{label}] başladı")
result = func(*args, **kwargs)
print(f"[{label}] bitti")
return result
return wrapper
return decorator
@announce("RAPOR")
def rapor_olustur():
print("Rapor hazırlanıyor")
rapor_olustur()Bu örnekte varsayımımız şu: dekoratör, ilk parametresi user olan fonksiyonlara uygulanacak.
from functools import wraps
class User:
def __init__(self, name, role):
self.name = name
self.role = role
def require_role(required_role):
def decorator(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if user.role != required_role:
raise PermissionError(f"'{required_role}' yetkisi gereklidir.")
return func(user, *args, **kwargs)
return wrapper
return decorator
@require_role("admin")
def raporu_gor(user):
return f"{user.name} raporu görüntülüyor"Yetki kontrolünü her fonksiyonun içine ayrı ayrı yazmak yerine ortaklaştırdık.
Böylece:
Ama dikkat:
Dekoratörler, kötü tasarımı sihirli biçimde düzeltmez. Sadece tekrar eden çapraz kesit davranışlarını toplamak için uygundur.
Dekoratörler özellikle şu durumlarda doğrudur:
Şu durumda dikkatli olunmalıdır:
wrapper içinde return unutmak*args, **kwargs kullanmamak@wraps kullanmamakDekoratör kullanmak her zaman avantaj sağlamaz; akışı okumayı zorlaştırıyorsa daha basit bir çözüm düşünülmelidir.
Dekoratörlerde fonksiyonun davranışını düzenledik.
Şimdi ise fonksiyonu, veri işleme adımlarını daha açık kurmak için kullanacağız.
Yeni sorumuz şudur:
Bir listedeki veriyi daha düzenli ve okunur biçimde nasıl dönüştürür, filtreler veya sıralarım?
lambda Nedir?lambda, kısa ve tek ifadeli anonim fonksiyon tanımlamaya yarar.
Temel biçim:
Örnek:
Ama burada önemli sınır şudur:
lambda, küçük ve yerel kullanım için uygundur; büyük mantıklar için değildir.
lambda Ne Zaman Uygun, Ne Zaman Değil?Uygun:
sorted(..., key=...) içinde kısa seçim yapmakmap veya filter içinde çok küçük dönüşümler yapmakUygun değil:
Özet kural:
Anlatmak zorlaşıyorsa def tercih edin.
map ÖrneğiPython 3’te map(...) sonucu doğrudan liste değildir; bu yüzden burada list(...) ile sardık.
Bu örnek teknik olarak doğrudur; ama burada şu soruyu sormalıyız:
Bunu daha okunabilir başka nasıl yazardık?
Birçok durumda bu sürüm daha doğrudan okunur.
Bu yüzden önemli nokta şudur:
Python’da her map kullanımı zorunlu olarak en iyi çözüm değildir.
filter ÖrneğiAnlamı nettir:
Burada da çoğu durumda liste üreteci daha doğal okunur.
Bu nedenle verilmek istenen mesaj şudur:
Araç seçimi kısalık için değil, okunabilirlik için yapılmalıdır.
reduce Nedir ve Neden Daha Dikkatli Kullanılır?reduce, listedeki elemanları adım adım birleştirerek tek sonuç üretir.
from functools import reduce
numbers = [1, 2, 3, 4]
toplam = reduce(lambda x, y: x + y, numbers)
print(toplam)Teknik olarak yararlıdır; ama ilk karşılaşmada adım adım takip etmek daha zor olabilir.
Ayrıca boş liste ve başlangıç değeri gibi durumlarda ayrıca karar vermek gerekir.
Örneğin basit toplama işlemlerinde çoğu zaman sum(numbers) daha doğrudan bir çözümdür.
Bu yüzden:
reduce bilinmeli; ama her indirgeme işinde ilk tercih olmak zorunda değildir.
sorted(key=...) KullanımıFonksiyonel düşüncenin en yararlı ve günlük örneklerinden biri sıralamadır:
students = [("Ahmet", 85), ("Mehmet", 92), ("Ayşe", 78)]
sorted_students = sorted(students, key=lambda student: student[1], reverse=True)
print(sorted_students)Burada lambda, çok küçük ve yerel bir iş yaptığı için uygundur.
| Araç | Temel amaç | Tipik soru |
|---|---|---|
map |
her elemana dönüşüm uygulamak | “Her elemanı neye çevireyim?” |
filter |
bazı elemanları seçmek | “Hangileri kalsın?” |
reduce |
çok elemanı tek sonuca indirmek | “Hepsini nasıl birleştireyim?” |
sorted(key=...) |
bir ölçüte göre sıralamak | “Neye bakarak sıralayayım?” |
Ama en üst ilke şudur:
Bir hafta sonra yeniden baktığınızda en hızlı anlayacağınız çözüm genelde daha iyidir.
@dekorator, aslında yeniden atama yapan daha okunabilir bir sözdizimidir.*args, **kwargs, return ve @wraps önemlidir.lambda küçük işler için iyidir; büyüyen mantıklarda def daha doğrudur.map ve filter yararlıdır; ama birçok günlük durumda liste üreteçleri daha doğal okunur.reduce güçlüdür; fakat her indirgeme işinde ilk tercih olmak zorunda değildir.filter ile hem liste üreteci ile yazın. Sonra hangi sürümün daha kolay okunduğunu iki cümleyle açıklayın.