Soru Tarihi
Dekoratörler, Python'un en güçlü araçlarından biridir ve işlevlerin veya yöntemlerin davranışını değiştirmeye olanak tanır. Bazen sadece bir işlevi "sarmak" yerine, dekoratörü parametrelerle (argümanlarla) ayarlamak gerekir. Bu durumlar, günlük günlüğü, zaman kontrolleri, erişim kısıtlamaları vb. durumlarda ortaya çıkar.
Problem
Normal dekoratörler yalnızca sarmak gereken bir işlev alır. Dekoratöre parametreler iletildiğinde, sözdizimi daha karmaşık hale gelir ve bu sıklıkla hatalara yol açar, özellikle işlevlerin iç içe geçmesi ve *args/**kwargs ile geçiş yapılması durumunda.
Çözüm
Parametreli dekoratör, daha yüksek bir sıradaki fonksiyonla uygulanır: önce argümanlarla dış "dekoratör" fonksiyonu çağrılır, ardından bu fonksiyon dekoratörü oluşturup geri döner, ve dekoratör ise işlevi alır ve sarma işlemini gerçekleştirir:
def repeat(n): def decorator(func): def wrapper(*args, **kwargs): result = None for _ in range(n): result = func(*args, **kwargs) return result return wrapper return decorator @repeat(3) def greet(name): print(f"Hello, {name}!") greet("Python") # Çıktı: Hello, Python! (3 kez)
Anahtar özellikler:
Parametreli dekoratörü neden normal dekoratör gibi uygulayamıyorsunuz?
Eğer @decorator uygularsanız, Python işlevi dekoratöre argüman olarak geçirir. Parantez eklediğinizde (@decorator()), Python önce fonksiyonu çağırır ve sadece onun sonucu dekoratör olarak yorumlanır.
def deco(func): # normal dekoratör: @deco def deco_with_args(arg): # argümanlı dekoratör: @deco_with_args(arg)
Parametreli dekoratörler, parametresiz dekoratörlerden ne şekilde farklıdır?
Parametresiz dekoratörler bir işlev alır; argümanlı dekoratörler ise bir işlev değil, parametreler alır ve kendilerinden dekoratör döndürür.
functools.wraps'ı nasıl doğru kullanmalıyım ve neden?
functools.wraps(func) sarma işlemi içinde, orijinal işlevin adını, belgelendirme dizgisini ve diğer meta verileri korur, aksi takdirde bu bilgiler wrapper'da değiştirilir, bu da hata ayıklamayı ve intospeksiyonu zorlaştırır.
import functools def deco_with_args(arg): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper return decorator
Parametreleri dikkate almadan ya da yanlış iç içe fonksiyon sayısıyla dekoratör oluşturmuş:
def log(level): def wrapper(func): # hata — wrapper daha derin olmalı print(f"Log: {level}") func() # İşlev dekoratör olarak döndürülmez return wrapper @log("INFO") def action(): print("Work!")
Artılar:
Eksiler:
functools.wraps ve doğru iç içe fonksiyonlar kullanımı:
import functools def timer(units): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): import time start = time.time() result = func(*args, **kwargs) end = time.time() if units == 'ms': duration = (end - start) * 1000 else: duration = end - start print(f"Duration: {duration:.4f} {units}") return result return wrapper return decorator @timer('ms') def op(): sum(range(1000)) op()
Artılar:
Eksiler: