Bu sorunun eski olduğunu biliyorum, ancak yorumların bazıları yeni ve uygulanabilir çözümlerin tümü temelde aynı olsa da, çoğu çok temiz veya okunması kolay değil.
Thobe'nin cevabının dediği gibi, her iki durumu da ele almanın tek yolu, her iki senaryoyu da kontrol etmektir. En kolay yol, tek bir argüman olup olmadığını ve bunun callabe olup olmadığını kontrol etmektir (NOT: dekoratörünüz yalnızca 1 argüman alırsa ve çağrılabilir bir nesne olursa ekstra kontroller gerekli olacaktır):
def decorator(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
else:
İlk durumda, normal bir dekoratörün yaptığını yaparsınız, aktarılan işlevin değiştirilmiş veya sarılmış bir sürümünü döndürürsünüz.
İkinci durumda, * args, ** kwargs ile iletilen bilgileri bir şekilde kullanan 'yeni' bir dekoratör döndürürsünüz.
Bu iyi ve hepsi, ancak yaptığınız her dekoratör için yazmak zorunda kalmak oldukça can sıkıcı olabilir ve o kadar temiz olmayabilir. Bunun yerine, dekoratörlerimizi yeniden yazmak zorunda kalmadan otomatik olarak değiştirebilmek güzel olurdu ... ama dekoratörler bunun için var!
Aşağıdaki dekoratör dekoratörünü kullanarak, dekoratörlerimizi bağımsız değişkenlerle veya bağımsız olarak kullanılabilecekleri şekilde çözebiliriz:
def doublewrap(f):
'''
a decorator decorator, allowing the decorator to be used as:
@decorator(with, arguments, and=kwargs)
or
@decorator
'''
@wraps(f)
def new_dec(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
return f(args[0])
else:
return lambda realf: f(realf, *args, **kwargs)
return new_dec
Şimdi, dekoratörlerimizi @doublewrap ile süsleyebiliriz ve onlar argümanlarla ve argümansız çalışacaklar, tek bir uyarı ile:
Yukarıda not ettim ama burada tekrar etmeliyim, bu dekoratördeki kontrol, bir dekoratörün alabileceği argümanlar hakkında bir varsayımda bulunur (yani tek, çağrılabilir bir argüman alamayacağı). Şu anda herhangi bir jeneratöre uygulanabilir hale getirdiğimiz için, akılda tutulmalı veya çelişecekse değiştirilmelidir.
Aşağıdakiler kullanımını göstermektedir:
def test_doublewrap():
from util import doublewrap
from functools import wraps
@doublewrap
def mult(f, factor=2):
'''multiply a function's return value'''
@wraps(f)
def wrap(*args, **kwargs):
return factor*f(*args,**kwargs)
return wrap
@mult
def f(x, y):
return x + y
@mult(3)
def f2(x, y):
return x*y
@mult(factor=5)
def f3(x, y):
return x - y
assert f(2,3) == 10
assert f2(2,5) == 30
assert f3(8,1) == 5*7
@redirect_outputdikkat çekici derecede bilgisizdir. Bunun kötü bir fikir olduğunu söyleyebilirim. İlk formu kullanın ve hayatınızı çok kolaylaştırın.