Python'da yüksek order fonksiyon dekoratörü, başka bir fonksiyonu (veya sınıfı) alan ve yeni bir fonksiyonu (veya yeni bir değiştirilmiş sınıfı) döndüren bir fonksiyondur. Dekoratörler, mevcut fonksiyonlara ek mantık (örneğin, loglama, önbellekleme, yetki kontrolü vb.) eklemeye olanak tanıyan "sarmalama" (wrapping) desenini uygulamak için sıklıkla kullanılır ve böylece orijinal kodu değiştirmeden çalışabiliriz.
Orijinal fonksiyonun adını, dokümantasyonunu ve diğer metadata’sını korumak için functools.wraps fonksiyonunun kullanılması önerilir:
import functools def log_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(f'Calling {func.__name__}') return func(*args, **kwargs) return wrapper @log_decorator def add(a, b): """İki sayıyı topla""" return a + b print(add(2, 3)) # Çıktı: Calling add 5 print(add.__name__) # Çıktı: add print(add.__doc__) # Çıktı: İki sayıyı topla
Anahtar nokta: functools.wraps kullanılmadan sarmalanmış fonksiyon, orijinalinin ismini, dokümantasyonunu ve diğer metadata'sını kaybedecektir, bu da hata ayıklama ve otomatik dokümantasyon üzerinde olumsuz bir etki yaratır.
Eğer bir fonksiyonu functools.wraps olmadan dekore ederseniz, name ve doc nitelikleri ile ne olur?
Cevap: Bunlar, içsel sarmalayıcı fonksiyondan (genellikle 'wrapper') miras alınacak ve orijinal metadata'yı kaybetmiş olacaksınız.
def simple_decorator(func): def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper @simple_decorator def f(): """Bu bir dokümantasyon yazısıdır""" pass print(f.__name__) # Çıktı: 'wrapper' (NE 'f') print(f.__doc__) # Çıktı: None (veya 'Bu bir dokümantasyon yazısıdır' değil)
Hikaye
Bir projede API uç noktalarını günlüğe almak için karmaşık bir dekoratör sistemi uygulandı, ancak functools.wraps kullanılmadı. Sonuç olarak, otomatik belge oluşturma (Swagger/OpenAPI) ve introspeksiyon araçları, tüm uç noktaların ismini 'wrapper' olarak gösteriyor ve dokümantasyon kaybolmuştu. Bu durum, destek, test etme ve bakım süreçlerini oldukça zorlaştırıyordu.
Hikaye
pytest kullanarak birim testleri yazarken, test fonksiyonlarının kendi dekoratörleri olmadan wraps ile dekore edilmediği için auto-discovery testlerde bir hata oluştu. Bu test fonksiyonları doğru bir şekilde keşfedilemedi çünkü name'leri hatalıydı. Sebep — pytest fonksiyonları isme göre arar.
Hikaye
İstisnaların yığın izinin (traceback) izlenmesi sırasında, yığın izinin her zaman "wrapper" üzerine işaret ettiğini görmek mümkündü ve hangi fonksiyonun hatayı çağırdığı anlaşılmıyordu, çünkü kök metadata'sı, kullanıcı dekoratörlerinde functools.wraps'ın eksikliğinden dolayı kaybolmuştu.