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.
Bu hafta ise şu soruya geçiyoruz:
“Davranışı, fonksiyon seviyesinde nasıl esnek hale getiririm?”
Bugünkü iki ana araç:
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ğunuBu konu, önceki haftaların doğal devamıdır:
Kısaca:
Geçen hafta sınıf davranışını şekillendirdik; bu hafta fonksiyon davranışını şekillendiriyoruz.
Bugün iki temel soruya cevap arıyoruz:
İlk sorunun ana cevabı: dekoratörler
İkinci sorunun ana araçları: fonksiyonel yaklaşımlar
Python’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.
Dekoratörü şöyle düşünmek faydalıdır:
wrapper vardır,wrapper içine gelir,Kısaca akış şöyledir:
çağrı -> wrapper -> orijinal fonksiyon -> wrapper -> sonuç
gibi tekrar eden işleri her fonksiyona tek tek yazmak yerine ortaklaştırır.
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:
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 en sık yapılan hatalardan biri de şudur:
func(...) çağrılırBu 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():
return "Merhaba"
print(selam.__name__) # wrapper@wraps(func) kullanıldığında bu isim bilgisi korunur ve çıktı selam olur.
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()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"Bu örnek, ilk parametresi user olan fonksiyonlar için yazılmıştır.
Yetki kontrolünü her fonksiyonun içine ayrı ayrı yazmak yerine ortaklaştırdık.
Bu bize şunu gösterir:
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 kullanmamakPratik ilke:
Dekoratör güçlüdür; ama okunabilirlikten pahalıya gelmemelidir.
Şimdi odağı biraz değiştiriyoruz.
Dekoratörlerde bir fonksiyonun etrafına yeni davranış ekledik.
Bu bölümde ise fonksiyonları, veri işlerken kullandığımız küçük araçlar gibi düşüneceğiz.
Yani artık şu soruya bakıyoruz:
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.
| 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?” |
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ğifilter(...) için de aynı durum geçerlidir: sonuç doğrudan liste olmadığı için list(...) kullanıldı.
Anlamı nettir:
Burada da çoğu durumda liste üreteci daha doğal okunur.
Bu nedenle burada verilmek istenen mesaj şudur:
Araç seçimi kısalık için değil, okunabilirlik için yapılmalıdır.
Python’da map ve filter bilinmelidir.
Ama günlük kullanımda birçok durumda liste üreteçleri daha doğal okunur.
Bu yüzden seçim yaparken şu soru daha yararlıdır:
Bu kodu bir hafta sonra en hızlı hangi biçimde okuyabilirim?
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 toplama 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.
products = [
{"name": "Laptop", "price": 50000, "stock": 5, "category": "Elektronik"},
{"name": "Mouse", "price": 1200, "stock": 25, "category": "Elektronik"},
{"name": "Kitap", "price": 350, "stock": 40, "category": "Kitap"},
]
electronics = [product for product in products if product["category"] == "Elektronik"]
prices = [product["price"] for product in electronics]
avg_price = sum(prices) / len(prices)
print(avg_price)Bu örnek özellikle şu açıdan önemli:
Yani gerçek veri işleme problemleri çoğu zaman bu üç adımın birleşimidir.
Pratik karar çerçevesi:
mapfiltersorted(key=...)reduceAma 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.Bilişsel yükü kontrollü tutmak için bugün şunlara ayrıntılı girmiyoruz:
@property, @classmethod, @staticmethod),Bunlar önemlidir; ancak bugünkü çekirdek kavram önce fonksiyon dekoratörlerini doğru anlamaktır.
filter ile hem liste üreteci ile yazın ve hangisinin daha okunur olduğunu tartışın.