在 Python 里,装饰器(decorator)就是那个写在函数头上的 @xxx。它能让一段"通用逻辑"(计时、日志、缓存、权限检查)只写一次,然后套用到任意多个函数上,而不用改动原函数的代码。
函数是对象:理解装饰器的地基
Python 中函数和数字、字符串一样是对象,可以被赋值给变量、当作参数传给别的函数。
def greet():
return "你好"
say = greet # 函数赋值给另一个变量,注意没有括号
print(say()) # 输出:你好
既然函数可以当参数传,那我们就可以写一个"接收函数、返回新函数"的函数——这就是装饰器。
手写第一个装饰器
下面的 timer 装饰器会给任何函数加上"运行前提示":
def announce(func):
def wrapper():
print("函数要开始运行了")
result = func()
print("函数运行结束了")
return result
return wrapper
@announce
def hello():
print("Hello!")
hello()
# 输出:
# 函数要开始运行了
# Hello!
# 函数运行结束了
@announce 只是 hello = announce(hello) 的语法糖:原函数被包进了 wrapper,调用 hello() 时实际执行的是 wrapper 里的三行逻辑。
处理参数和返回值
被装饰的函数往往带参数,这时让 wrapper 接收 *args, **kwargs 并原样转发即可,兼容任何签名:
def announce(func):
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__},参数 {args}")
return func(*args, **kwargs)
return wrapper
@announce
def add(a, b):
return a + b
print(add(3, 5)) # 输出:调用 add,参数 (3, 5),然后输出:8
实用示例:给函数计时
装饰器最常见的用途之一是统计耗时,标准库 time 就够了:
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
cost = time.perf_counter() - start
print(f"{func.__name__} 耗时 {cost:.4f} 秒")
return result
return wrapper
@timer
def slow_sum():
total = 0
for i in range(2_000_000):
total += i
return total
print(slow_sum())
# 输出:slow_sum 耗时 0.09xx 秒(数值因机器而异),以及一个很大的和
保留原函数的名字
被装饰后,__name__ 会变成 wrapper。加上标准库的 functools.wraps 可以保留原函数的元信息,这是写装饰器的标准姿势:
from functools import wraps
def announce(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
小结
装饰器本质是"接收函数并返回新函数"的高阶函数,@ 语法糖等价于 f = decorator(f);用 *args, **kwargs 转发参数可适配任意函数,用 functools.wraps 保留原函数信息。掌握之后,日志、计时、缓存这类横切逻辑就能一处编写、处处复用。建议从给自己常用的函数加一个 @timer 开始练习。
🔒 当前域名不开放评论功能