在 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 指向包装后的可调用对象。

装饰器的结构

say_hi = my_decorator(say_hi)(@ 做的事) wrapper:包装后返回的新函数 func 原函数
装饰器在外层增加日志、计时等逻辑,再调用 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 模块的序列化与反序列化。