在 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 开始练习。