Saya memiliki dekorator seperti di bawah ini.
def myDecorator(test_func):
return callSomeWrapper(test_func)
def callSomeWrapper(test_func):
return test_func
@myDecorator
def someFunc():
print 'hello'
Saya ingin meningkatkan dekorator ini untuk menerima argumen lain seperti di bawah ini
def myDecorator(test_func,logIt):
if logIt:
print "Calling Function: " + test_func.__name__
return callSomeWrapper(test_func)
@myDecorator(False)
def someFunc():
print 'Hello'
Tetapi kode ini memberikan kesalahan,
TypeError: myDecorator () mengambil tepat 2 argumen (1 diberikan)
Mengapa fungsinya tidak otomatis diteruskan? Bagaimana cara meneruskan fungsi secara eksplisit ke fungsi dekorator?
Jawaban:
Karena Anda memanggil dekorator seperti sebuah fungsi, ia perlu mengembalikan fungsi lain yang merupakan dekorator sebenarnya:
def my_decorator(param): def actual_decorator(func): print("Decorating function {}, with parameter {}".format(func.__name__, param)) return function_wrapper(func) # assume we defined a wrapper somewhere return actual_decorator
Fungsi luar akan diberi argumen apa pun yang Anda teruskan secara eksplisit, dan harus mengembalikan fungsi dalam. Fungsi bagian dalam akan diberikan fungsi untuk menghias, dan mengembalikan fungsi yang dimodifikasi.
Biasanya Anda ingin dekorator mengubah perilaku fungsi dengan membungkusnya dalam fungsi pembungkus. Berikut adalah contoh yang secara opsional menambahkan logging saat fungsinya dipanggil:
def log_decorator(log_enabled): def actual_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): if log_enabled: print("Calling Function: " + func.__name__) return func(*args, **kwargs) return wrapper return actual_decorator
Itu
functools.wraps
panggilan salinan hal-hal seperti nama dan docstring untuk fungsi wrapper, untuk membuatnya lebih mirip dengan fungsi asli.Contoh penggunaan:
>>> @log_decorator(True) ... def f(x): ... return x+1 ... >>> f(4) Calling Function: f 5
sumber
functools.wraps
disarankan - ini mempertahankan nama asli, docstring, dll. Dari fungsi yang dibungkus.myDecorator
sini) sebagai dekorator. Ini nyaman bagi pengguna dekorator, tetapi bisa membingungkan saat Anda mencoba menulisnya.log_decorator
mengambil argumen default, Anda tidak dapat menggunakannya@log_decorator
, itu pasti@log_decorator()
Hanya untuk memberikan sudut pandang yang berbeda: sintaks
@expr def func(...): #stuff
setara dengan
def func(...): #stuff func = expr(func)
Secara khusus,
expr
bisa apa saja yang Anda suka, asalkan terevaluasi menjadi callable. Secara khusus ,expr
dapat menjadi pabrik dekorator: Anda memberikan beberapa parameter dan itu memberi Anda dekorator. Jadi mungkin cara yang lebih baik untuk memahami situasi Anda adalah sebagaidec = decorator_factory(*args) @dec def func(...):
yang kemudian dapat disingkat menjadi
@decorator_factory(*args) def func(...):
Tentu saja, karena terlihat seperti
decorator_factory
dekorator, orang cenderung menamakannya untuk mencerminkan hal itu. Yang bisa membingungkan saat Anda mencoba mengikuti tingkat tipuan.sumber
Hanya ingin menambahkan beberapa trik berguna yang memungkinkan argumen dekorator menjadi opsional. Ini juga akan memungkinkan untuk menggunakan kembali dekorator dan mengurangi sarang
import functools def myDecorator(test_func=None,logIt=None): if not test_func: return functools.partial(myDecorator, logIt=logIt) @functools.wraps(test_func) def f(*args, **kwargs): if logIt==1: print 'Logging level 1 for {}'.format(test_func.__name__) if logIt==2: print 'Logging level 2 for {}'.format(test_func.__name__) return test_func(*args, **kwargs) return f #new decorator myDecorator_2 = myDecorator(logIt=2) @myDecorator(logIt=2) def pow2(i): return i**2 @myDecorator def pow3(i): return i**3 @myDecorator_2 def pow4(i): return i**4 print pow2(2) print pow3(2) print pow4(2)
sumber
Hanya cara lain melakukan dekorator. Saya menemukan cara ini yang paling mudah untuk membungkus kepala saya.
class NiceDecorator: def __init__(self, param_foo='a', param_bar='b'): self.param_foo = param_foo self.param_bar = param_bar def __call__(self, func): def my_logic(*args, **kwargs): # whatever logic your decorator is supposed to implement goes in here print('pre action baz') print(self.param_bar) # including the call to the decorated function (if you want to do that) result = func(*args, **kwargs) print('post action beep') return result return my_logic # usage example from here on @NiceDecorator(param_bar='baaar') def example(): print('example yay') example()
sumber
Sekarang jika Anda ingin memanggil fungsi
function1
dengan dekoratordecorator_with_arg
dan dalam hal ini baik fungsi dan dekorator mengambil argumen,def function1(a, b): print (a, b) decorator_with_arg(10)(function1)(1, 2)
sumber