Khi bạn sử dụng một trình trang trí, bạn đang thay thế một chức năng bằng một chức năng khác. Nói cách khác, nếu bạn có một người trang trí
def logged(func):
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
sau đó khi bạn nói
@logged
def f(x):
"""does some math"""
return x + x * x
nó giống hệt như nói
def f(x):
"""does some math"""
return x + x * x
f = logged(f)
và chức năng của bạn f
được thay thế bằng chức năng with_logging
. Thật không may, điều này có nghĩa là nếu sau đó bạn nói
print(f.__name__)
nó sẽ in with_logging
vì đó là tên của chức năng mới của bạn. Trong thực tế, nếu bạn nhìn vào chuỗi doc f
, nó sẽ trống vì with_logging
không có chuỗi doc, và do đó, chuỗi doc bạn đã viết sẽ không còn ở đó nữa. Ngoài ra, nếu bạn nhìn vào kết quả pydoc cho hàm đó, nó sẽ không được liệt kê là lấy một đối số x
; thay vào đó, nó sẽ được liệt kê là lấy *args
và **kwargs
bởi vì đó là những gì with_logging mất.
Nếu sử dụng một trình trang trí luôn có nghĩa là mất thông tin này về một chức năng, nó sẽ là một vấn đề nghiêm trọng. Đó là lý do tại sao chúng ta có functools.wraps
. Điều này có một chức năng được sử dụng trong một trình trang trí và thêm chức năng sao chép tên hàm, chuỗi, danh sách đối số, v.v. Và vì wraps
chính nó là một trình trang trí, mã sau đây thực hiện đúng:
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'