Syntax:
- Decorator
- a callable that takes a function
funcand returns a new function - 一个可调用对象,接收
func,返回一个“增强后的 func”
- a callable that takes a function
- syntactic sugar (语法糖)
- 更好写、更好读”的语法形式,但它不增加新能力(= 更基础的写法)
- Example:
@decois just a nicer way to writehello = deco(hello)
*args和**kwargs:*args: var-positional arguments (可变位置参数)**kwargs: var-keyword arguments (可变关键字参数)
Python, everything is an object (Numbers, Strings, Functions)
hello:是“函数本身”(function object)hello():是“调用这个函数”(execute / run it)
Example 1: Simple decorator (no arguments)
def deco(func):
def wrapper():
print("before") # 包装:原函数前做点事
func() # 调原函数
print("after") # 包装:原函数后做点事
return wrapper
@deco # hello = deco(hello)
def hello():
print("hello")
hello()Return:
before
hello
after
Example 2: Decorator (with arguments)
@decois syntactic sugar foradd = deco(add)
def deco(func):
def wrapper(a, b): # wrapper accepts the same args
print("before", a, b)
result = func(a, b) # call original function with args
print("after", result)
return result
return wrapper
@deco # add = deco(add)
def add(a, b):
return a + b
print(add(1, 2))Example 3: 通用 Decorator(支持任意参数)
*argsand**kwargsallow a function to accept an arbitrary number of arguments.*args和**kwargs是 Python 的“固定套路”(约定俗成 + 语法规则)*args位置参数的“打包/收集”args是个 tuple- 用来接收你传进来的 所有位置参数
**kwargs关键字参数的“打包/收集”kwargs是个 dict- 用来接收你传进来的 所有关键字参数
def deco(func):
def wrapper(*args, **kwargs):
print("before", args, kwargs)
result = func(*args, **kwargs)
print("after", result)
return result
return wrapperExample 4: Decorator to measure runtime
- time.perf_counter()
import time
def timing(func):
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
elapsed_ms = (end_time - start_time) * 1000
print(f"[{func.__name__}] runtime: {elapsed_ms:.3f} ms")
return result
return wrapper
@timing
def hello(a, b):
time.sleep(1)
print(a, b)
hello("Alan", "Wang")
print(hello.__name__) # wrapper, 不方便(undesired)Note:
- Uses
time.perf_counter()(better for timing thantime.time()). - Uses
@wraps(func)to keep the original function’s name/docstring. - add @wraps(func)
- 让被装饰后的函数“看起来还是原函数”,不然它会变成
wrapper,很多信息会丢、调试会变难。
- 让被装饰后的函数“看起来还是原函数”,不然它会变成
import time
from functools import wraps
def timing(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
try:
return func(*args, **kwargs)
finally:
end = time.perf_counter()
elapsed_ms = (end - start) * 1000
print(f"[{func.__name__}] runtime: {elapsed_ms:.3f} ms")
return wrapper
@timing
def hello(a, b):
time.sleep(1)
print(a, b)
hello("Alan", "Wang")
print(hello.__name__) # hello (not wrapper)Example 5: Decorator with parameters (@repeat(times))
repeat(times)先接收参数(比如 5),并把times记住(闭包)- 然后返回一个真正的 decorator:
decorate(func),它才接收函数并返回wrapper
from functools import wraps
def repeat(times):
def decorate(func):
@wraps(func) # ✅ 这里要传 func
def wrapper(*args, **kwargs):
result = None
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorate
@repeat(5)
def hello(name):
print(name)
hello("Alan")