Menu

Python Dekoratörler: @fonksiyonlar, Argümanlar ve functools.wraps

Python dekoratörleri aslında nedir, kendininkini nasıl yazarsın ve onları kullanmaya değer kılan kalıplar (argümanlar, üst üste bindirme, wraps).

Bir Dekoratör, Bir Fonksiyonu Saran Bir Fonksiyondur

Bu cümle soyut geliyor ama mekanik basit. Bir dekoratör bir fonksiyon alır, bir fonksiyon döndürür. Döndürdüğü fonksiyon genelde orijinali çağırır ve çağrının etrafına biraz ekstra davranış sarar.

Mümkün olan en kısa örnek:

main.py
Output
Click Run to see the output here.

shout dekoratördür. Bir fonksiyon alır (greet), orijinali çağıran ve sonucu büyük harfe çeviren yeni bir fonksiyon (wrapper) kurar ve onu döndürür. greet = shout(greet) ataması, orijinali sarılmış versiyonla değiştirir.

Bu yeniden atama kalıbı o kadar yaygındır ki Python ona özel bir söz dizimi verdi.

@ Bir Yeniden Atamanın Şekerlenmiş Halidir

Bir def satırının üstündeki @name, fonksiyon tanımlandıktan hemen sonra gelen name = name(...)'e eşdeğerdir:

main.py
Output
Click Run to see the output here.

@shout, "bu fonksiyona shout dekoratörünü uygula" diye okunur. Python, def'in hemen ardından greet = shout(greet)'i çalıştırır — öncekiyle aynı mekanik, daha az yazma.

@name gördüğünde zihnen function = name(function) olarak değiştir. Söz diziminin tüm anlamı budur.

Argümanları Yönetmek

Çoğu fonksiyon argüman alır. Kullanılabilir bir dekoratör bunları iletir. Deyim *args, **kwargs'tır — Python'un her türlü argümanı kabul etme yolu — çünkü sarmalayıcı sarılan fonksiyonun ne beklediğini umursamamalıdır:

main.py
Output
Click Run to see the output here.

*args tüm konumsal argümanları yakalar. **kwargs tüm anahtar kelime argümanlarını yakalar. Sarmalayıcı her şeyi değişmeden sarılan fonksiyona iletir, sonra dekoratörün varlık sebebi olan ekstra işi yapar — burada sonucu büyük harfe çevirmek.

Gerçek dekoratörlerin çoğu bu şekli alır.

Daha Yararlı Bir Örnek: Zamanlama

Bir fonksiyonun ne kadar sürdüğünü yazdırmak:

main.py
Output
Click Run to see the output here.

Kalıp — çağrıdan önce bir şey çalıştır, çağrıdan sonra bir şey çalıştır — dekoratörlerin çoğunun yaptığıdır. Log tutma, yetki kontrolleri, yeniden deneme ve girdi doğrulama hepsi aynı şekli izler.

Orijinal Kimliği Korumak: functools.wraps

Bir fonksiyonu dekore etmek onu değiştirir, yani sarılan fonksiyon orijinal __name__ ve __doc__ özniteliklerini kaybeder:

main.py
Output
Click Run to see the output here.

greet.__name__ artık "wrapper" ve docstring yok. Bu help()'i, traceback'leri ve fonksiyonu inceleyen her aracı bozar.

Çözüm tek satır: iç fonksiyonda @functools.wraps(func) metadata'yı karşıya kopyalar.

main.py
Output
Click Run to see the output here.

İç fonksiyona her zaman @wraps(func) ekle. Hiçbir maliyeti yok ve ileride şaşırtıcı hata ayıklama seanslarından kurtarır.

Argümanlı Dekoratörler

Bazen dekoratörün kendisi yapılandırma ister — "bu fonksiyonu 3 kez dene," "DEBUG seviyesinde logla." Bu, bir kat daha iç içelik demek: argümanları alan ve bir dekoratör döndüren bir dış fonksiyon.

main.py
Output
Click Run to see the output here.

Üç kat çok geliyor. Dıştan oku:

  1. repeat(times=3) bir fonksiyon çağrısıdır. decorator'ı döndürür.
  2. decorator gerçek dekoratördür — bir fonksiyon alır ve sarılmış birini döndürür.
  3. wrapper, çağrı zamanında çalışan sarılmış fonksiyondur.

Bu şekil, @retry(times=5), @cache(maxsize=100) ve @app.route("/users") gibi framework dekoratörlerinin arkasındaki güçtür. Üç katlı kalıbı bir kez gördün mü, tüm aile aynı şekilde okunur.

Dekoratörleri Üst Üste Koymak

Bir fonksiyona birden fazla dekoratör uygulayabilirsin. Alttan üste doğru yığılırlar — def'e en yakın olan önce çalışır:

main.py
Output
Click Run to see the output here.

add_exclaim önce sarar ve ! ekler. Sonra shout onu sarar ve her şeyi büyük harfe çevirir. Çıktı HI, ROSA!'dır.

Sıra önemlidir. Yığını ters çevirirsen yine HI, ROSA! alırsın ama ünlem büyük harfe çevirmeden sonra eklenir — burada görsel olarak aynı, ama JSON'ı biçimlendiren bir dekoratör düşün: girdiyi loglayan bir dekoratörden önce ya da sonra çalıştırmak çok farklı sonuçlar üretebilir.

Karşılaşacağın Yerleşik Dekoratörler

Python ve standart kütüphanesi, gerçek kodda karşılaşacağın bir avuç dekoratörle gelir:

main.py
Output
Click Run to see the output here.
  • @property, bir metodu hesaplanmış bir özniteliğe çevirir.
  • @staticmethod, self ya da cls kullanmayan bir metodu işaretler.
  • @classmethod, örnek yerine sınıfı cls olarak alır — alternatif yapıcılar için harikadır.
  • @functools.lru_cache sonuçları hafızaya alır; aynı argümanlarla tekrar eden çağrılar bir cache'e düşer.

Framework dekoratörleri (@app.route, @pytest.fixture, @dataclass) aynı makineyi izler. Özel bir şey yok — sadece fonksiyonları saran fonksiyonlar.

Ne Zaman Yaz, Ne Zaman Yazma

Aynı davranışı birçok fonksiyona uygulamak istediğinde bir dekoratör yaz — zamanlama, log tutma, yeniden deneme, yetkilendirme kontrolleri. Bütün amaç, davranışın fonksiyonun gövdesi dışında kalmasıdır.

Dekoratörü atla:

  • Davranış tek bir belirli fonksiyona aitse. Onu fonksiyona koy.
  • Sadece test için istiyorsan. Bir fixture ya da parametre daha nettir.
  • Dört ya da beşini üst üste koymaya çekildiğinde. O noktada kontrol akışı dekoratör zincirinde gizlenir — okuyan kişi, aslında neyin çalıştığını görmek için her katmanı çözmek zorundadır. Basit bir yardımcı fonksiyon daha iyi okunabilir.

Dekoratörler keskin bir araçtır. İyi kullanıldığında kodu DRY, niyeti açık tutar. Kötü kullanıldığında programın ne yaptığını gizler. Birini kullanıp kullanmamaya karar verirken "açık olan" tarafa eğil.

Sırada: Tip İpuçları

Dekoratörler, tip ipuçlarıyla vahşi doğada sıkça karşılaştığın yerlerdendir — wrapper fonksiyonları genellikle imzalarını annote eder. Tip ipuçları hızlı geri dönüş sağlayan küçük bir özellik ve sırada.

Sıkça Sorulan Sorular

Python'da dekoratör nedir?

Bir dekoratör, başka bir fonksiyonu alan ve yeni bir fonksiyon döndüren bir fonksiyondur — genellikle orijinali ekstra davranışla saran bir fonksiyon. Bir def satırının üstüne @decorator_name yazarak uygularsın. @ söz dizimi, func = decorator_name(func)'un kısaltmasıdır.

Python'da dekoratörler ne için kullanılır?

Bir fonksiyonun gövdesini değiştirmeden etrafına davranış eklemek için — log tutma, zamanlama, önbellekleme, kimlik doğrulama kontrolleri, girdi doğrulama, yeniden deneme. Framework'ler bunları yoğun kullanır: Flask'ta @app.route(...), pytest'te @pytest.fixture, yerleşik olarak @property ve @staticmethod.

Kendi dekoratörümü yazabilir miyim?

Evet. Bir dekoratör, sadece bir fonksiyon alan ve bir fonksiyon döndüren bir fonksiyondur. Çoğu özel dekoratör, orijinal çağrıyı öncesinde, sonrasında ya da etrafında bir şey yapan küçük bir iç fonksiyona sarar. Orijinal fonksiyonun adını ve docstring'ini korumak için iç fonksiyonda functools.wraps kullan.

Coddy ile kodlamayı öğren

BAŞLA