Saat Anda menggunakan dekorator, Anda mengganti satu fungsi dengan yang lain. Dengan kata lain, jika Anda memiliki dekorator
def logged(func):
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
lalu ketika Anda mengatakannya
@logged
def f(x):
"""does some math"""
return x + x * x
persis sama dengan mengatakan
def f(x):
"""does some math"""
return x + x * x
f = logged(f)
dan fungsi Anda f
diganti dengan fungsi with_logging
. Sayangnya, ini berarti jika Anda mengatakannya
print(f.__name__)
itu akan dicetak with_logging
karena itulah nama fungsi baru Anda. Bahkan, jika Anda melihat docstring untuk f
, itu akan kosong karena with_logging
tidak memiliki docstring, sehingga docstring yang Anda tulis tidak akan ada lagi. Juga, jika Anda melihat hasil pydoc untuk fungsi itu, itu tidak akan terdaftar sebagai mengambil satu argumen x
; alih-alih itu akan terdaftar sebagai pengambilan *args
dan **kwargs
karena itulah yang mengambil with_logging.
Jika menggunakan dekorator selalu berarti kehilangan informasi ini tentang suatu fungsi, itu akan menjadi masalah serius. Itu sebabnya kami punya functools.wraps
. Ini mengambil fungsi yang digunakan dalam dekorator dan menambahkan fungsi menyalin di atas nama fungsi, docstring, daftar argumen, dll. Dan karena wraps
itu sendiri adalah dekorator, kode berikut melakukan hal yang benar:
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print(f.__name__) # prints 'f'
print(f.__doc__) # prints 'does some math'