Python中内置装饰器

发布于:2025-09-11 ⋅ 阅读:(15) ⋅ 点赞:(0)

Python 装饰器(Decorator)是一种强大的语法,用于在不修改原函数代码的前提下,动态地增强函数的功能。它本质上是一个高阶函数(接收函数作为参数,并返回一个新函数),通常用于日志记录、权限验证、缓存等场景。

一、装饰器的核心原理

装饰器的作用可以概括为:“包装”原函数,在原函数执行前后添加额外逻辑,同时不改变原函数的调用方式

基本流程:

  1. 定义一个装饰器函数(接收被装饰的函数作为参数)
  2. 在装饰器内部定义一个“包装函数”(实现增强逻辑)
  3. 返回这个包装函数替换原函数

二、基础示例:最简单的装饰器

下面通过一个“计时装饰器”示例,理解装饰器的基本用法:

# 1. 定义装饰器:计算函数执行时间
def timer_decorator(func):
    # 3. 定义包装函数(包装原函数)
    def wrapper():
        import time
        start = time.time()  # 执行前:记录开始时间
        func()  # 调用原函数
        end = time.time()    # 执行后:记录结束时间
        print(f"函数执行耗时:{end - start:.2f}秒")
    # 4. 返回包装函数(替换原函数)
    return wrapper

# 2. 使用装饰器:用@语法修饰目标函数
@timer_decorator
def slow_function():
    # 模拟耗时操作
    import time
    time.sleep(1)
    print("函数执行完毕")

# 调用被装饰后的函数(调用方式不变)
slow_function()

输出:

函数执行完毕
函数执行耗时:1.00秒

关键点

  • @timer_decorator 等价于 slow_function = timer_decorator(slow_function)
  • 调用 slow_function() 时,实际执行的是装饰器返回的 wrapper() 函数
  • 原函数 slow_function 的功能被保留,同时新增了“计时”功能

三、带参数的函数如何装饰?

如果被装饰的函数有参数,需要在包装函数中传递参数:

def log_decorator(func):
    # 包装函数接收任意参数 *args 和 **kwargs
    def wrapper(*args, **kwargs):
        print(f"调用函数:{func.__name__},参数:{args}, {kwargs}")
        result = func(*args, **kwargs)  # 传递参数给原函数
        print(f"函数返回值:{result}")
        return result  # 返回原函数的结果
    return wrapper

@log_decorator
def add(a, b):
    return a + b

# 调用带参数的函数
add(3, 5)

输出:

调用函数:add,参数:(3, 5), {}
函数返回值:8

说明

  • *args 接收任意数量的位置参数(如 3, 5
  • **kwargs 接收任意数量的关键字参数(如 a=3, b=5
  • 确保包装函数的参数能覆盖原函数的所有可能参数

四、带参数的装饰器

装饰器本身也可以接收参数,用于动态调整装饰逻辑。需要在原有装饰器外层再嵌套一层函数:

# 带参数的装饰器:允许指定日志前缀
def prefix_log_decorator(prefix):
    # 第一层:接收装饰器参数
    def decorator(func):
        # 第二层:接收被装饰函数
        def wrapper(*args, **kwargs):
            print(f"[{prefix}] 调用函数:{func.__name__}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

# 使用带参数的装饰器
@prefix_log_decorator(prefix="INFO")
def greet(name):
    return f"Hello, {name}!"

greet("kk")

输出:

[INFO] 调用函数:greet

执行逻辑
@prefix_log_decorator("INFO") 等价于:

greet = prefix_log_decorator("INFO")(greet)

下例可以验证上述执行逻辑:

def prefix_log_decorator(prefix):
    # 第一层:接收装饰器参数
    def decorator(func):
        # 第二层:接收被装饰函数
        def wrapper(*args, **kwargs):
            print(f"[{prefix}] 调用函数:{func.__name__}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

def AaddB(a,b):
    print(a+b)

prefix_log_decorator("INFO")(AaddB)(1,2)

五、保留原函数信息

装饰器会默认替换原函数的元信息(如 __name____doc__),可以用 functools.wraps 修复:

import functools

def my_decorator(func):
    @functools.wraps(func)  # 保留原函数信息
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def example():
    """这是示例函数的文档字符串"""
    pass

print(example.__name__)  # 输出:example(而非 wrapper)
print(example.__doc__)   # 输出:这是示例函数的文档字符串

六、常见应用场景

  1. 日志记录:自动记录函数调用信息、参数、返回值
  2. 权限验证:在函数执行前检查用户权限
  3. 缓存:缓存函数结果,避免重复计算
  4. 性能测试:统计函数执行时间
  5. 输入验证:检查函数参数是否符合要求

总结

  • 装饰器是“包装函数”的工具,核心是不修改原函数代码却能增强其功能
  • 基础语法:@装饰器名 放在函数定义上方
  • 处理参数:用 *args, **kwargs 适配任意参数的函数
  • 带参数的装饰器:需要多一层函数嵌套
  • 最佳实践:用 functools.wraps 保留原函数信息

装饰器是 Python 中“开闭原则”(对扩展开放,对修改关闭)的典型实现,在框架(如 Flask、Django)中被广泛使用。


网站公告

今日签到

点亮在社区的每一天
去签到