在 Python 中,装饰器用一个可调用对象包装另一个函数,在不修改被包装函数源码的前提下增加行为,例如记录日志、统计耗时或权限检查。@装饰器 为语法糖,等价于定义后将 函数名 = 装饰器(函数名) 重新绑定。装饰器实现上常返回内层函数,即闭包。本节说明基本写法、带参函数的装饰及元信息保留。
基本装饰器
def my_decorator(func):
def wrapper():
print("函数执行前")
func()
print("函数执行后")
return wrapper
@my_decorator
def say_hi():
print("Hi!")
say_hi()输出:
函数执行前
Hi!
函数执行后在 def say_hi 上方写 @my_decorator 时,解释器执行 say_hi = my_decorator(say_hi),此后名称 say_hi 指向包装后的可调用对象。
装饰器的结构
装饰器在外层增加日志、计时等逻辑,再调用 func;@ 等价于 say_hi = my_decorator(say_hi)。
def my_decorator(func):
def wrapper():
func()
return wrapper
@my_decorator
def f1():
pass
def f2():
pass
f2 = my_decorator(f2)
使用 @ 与手工赋值两种方式效果相同。
被装饰函数带参数
包装函数须用 *args、**kwargs 转发实参,并 return 原函数的返回值:
def log(func):
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__}, 参数={args}")
return func(*args, **kwargs)
return wrapper
@log
def add(a, b):
return a + b
print(add(3, 5))计时示例
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
cost = time.time() - start
print(f"{func.__name__} 耗时 {cost:.4f} 秒")
return result
return wrapper
@timer
def slow_work():
time.sleep(0.1)
return "done"
print(slow_work())保留原函数元信息
直接替换后,__name__、__doc__ 可能变为 wrapper 的。使用 functools.wraps 可将原函数的元数据复制到包装函数:
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@my_decorator
def hello():
"""打个招呼"""
print("hi")
print(hello.__name__)
print(hello.__doc__)常见错误
- 包装函数未声明
*args/**kwargs时,原函数一旦需要参数将报错。 - 调用原函数后未
return时,调用方得到None。 - 未使用
wraps时,调试输出与帮助文档可能与原函数名不一致。
本节说明了 Python 中装饰器的含义与实现要点。下一节介绍JSON:json 模块的序列化与反序列化。