Saya tahu pertanyaan ini sudah lama, tetapi beberapa komentar baru, dan sementara semua solusi yang layak pada dasarnya sama, kebanyakan tidak terlalu bersih atau mudah dibaca.
Seperti jawaban thobe katakan, satu-satunya cara untuk menangani kedua kasus tersebut adalah dengan memeriksa kedua skenario. Cara termudah adalah dengan memeriksa untuk melihat apakah ada satu argumen dan itu adalah callabe (CATATAN: pemeriksaan tambahan akan diperlukan jika dekorator Anda hanya mengambil 1 argumen dan kebetulan itu adalah objek yang dapat dipanggil):
def decorator(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
else:
Dalam kasus pertama, Anda melakukan apa yang dilakukan dekorator normal mana pun, mengembalikan versi modifikasi atau gabungan dari fungsi yang diteruskan.
Dalam kasus kedua, Anda mengembalikan dekorator 'baru' yang entah bagaimana menggunakan informasi yang diteruskan dengan * args, ** kwargs.
Ini bagus dan semuanya, tetapi harus menuliskannya untuk setiap dekorator yang Anda buat bisa sangat mengganggu dan tidak sebersih. Alih-alih, alangkah baiknya dapat secara otomatis memodifikasi dekorator kami tanpa harus menulis ulang ... tapi itulah gunanya dekorator!
Dengan menggunakan dekorator dekorator berikut, kita dapat mendeokrasi dekorator kita sehingga bisa digunakan dengan atau tanpa argumen:
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
Sekarang, kita dapat menghias dekorator kita dengan @doublewrap, dan mereka akan bekerja dengan dan tanpa argumen, dengan satu peringatan:
Saya sebutkan di atas tetapi harus diulangi di sini, centang di dekorator ini membuat asumsi tentang argumen yang dapat diterima dekorator (yaitu tidak dapat menerima satu pun, argumen yang dapat dipanggil). Karena kami membuatnya dapat diterapkan ke generator apa pun sekarang, itu perlu diingat, atau dimodifikasi jika akan bertentangan.
Berikut ini menunjukkan penggunaannya:
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_outputsangat tidak informatif. Saya menyarankan itu ide yang buruk. Gunakan formulir pertama dan sederhanakan hidup Anda.