【Python进阶】07 装饰器基础

0 阅读4分钟

🐍 Python 装饰器基础笔记

1. 核心概念与思维导图

装饰器本质上是一个高阶函数,也是闭包的一种特例应用。它允许你在不修改原函数代码、不改变原函数调用方式的前提下,为函数动态添加额外功能(如日志、计时、权限校验等)。

🧠 知识点思维导图

image-20260423194856025.png


2. 基础装饰器:无参模式

这是最简单的装饰器形式,适用于不需要额外配置的场景。

2.1 核心结构

  1. 外层函数:接收被装饰的函数 func
  2. 内层函数 (Wrapper):定义增强逻辑,调用 func
  3. 返回:返回内层函数对象。

2.2 代码示例:登录与退出功能

def decorator(func):
    def wrapper():
        print(" 登陆功能 (前置增强)")
        func()  # 执行原函数
        print(" 退出登录 (后置增强)")
    return wrapper

# 使用语法糖 @decorator 等价于 comment = decorator(comment)
@decorator
def comment():
    print("评论功能")

@decorator
def download():
    print("下载功能")

# 调用
comment()
# 输出:
# 登陆功能
# 评论功能
# 退出登录

3. 进阶一:处理被装饰函数的参数

如果原函数需要参数,装饰器的内层函数 wrapper 必须能够接收这些参数,并使用 *args**kwargs 透传给原函数。

3.1 代码示例:通用日志记录

import time

def logging(func):
    def wrapper(*args, **kwargs):
        # 1. 前置处理:打印函数名
        print(f"日志信息: {func.__name__} 开始执行")
        
        # 2. 执行原函数:必须传递参数
        func(*args, **kwargs)
        
        # 3. 后置处理
        print(f"日志信息: {func.__name__} 结束执行")
    return wrapper

@logging
def sum_num(*args, **kwargs):
    result = 0
    result += sum(args)
    result += sum(kwargs.values())
    print(f"计算结果是:{result}")

# 调用带参函数
sum_num(1, 2, 3, a=1, b=2)

4. 进阶二:处理返回值

很多时候我们需要获取原函数的执行结果。装饰器的 wrapper 必须接收原函数的返回值,并将其返回,否则调用被装饰的函数将得到 None

4.1 代码示例:保留返回值

def logging(func):
    def wrapper(*args, **kwargs):
        print(f"日志信息: {func.__name__} 开始执行")
        
        # 关键点:接收返回值
        result = func(*args, **kwargs)
        
        print("这里是函数执行中...")
        print(f"日志信息: {func.__name__} 结束执行")
        
        # 关键点:返回结果
        return result
    return wrapper

@logging
def sum_num(*args, **kwargs):
    result = sum(args) + sum(kwargs.values())
    return result

# 验证返回值
res = sum_num(1, 2, a=1)
print(f"最终获取到的返回值: {res}") 

5. 进阶三:装饰器本身带参数

如果装饰器需要根据不同的配置(如日志级别、重试次数)来改变增强逻辑,就需要三层嵌套

5.1 结构解析

  1. 最外层:接收装饰器参数 (flag),返回装饰器。
  2. 中间层:接收被装饰函数 (func),返回 wrapper。
  3. 最内层:接收函数参数 (*args),执行逻辑。

5.2 代码示例:可配置的日志级别

def logging(flag):  # 1. 接收装饰器参数
    def decorator(func):  # 2. 接收被装饰函数
        def wrapper(*args, **kwargs):  # 3. 接收函数调用参数
            if flag == "info":
                print(f"ℹ️ 日志信息: {func.__name__} 开始执行")
            elif flag == "warn":
                print(f"⚠️ 警告信息: {func.__name__} 开始执行")
            
            result = func(*args, **kwargs)
            
            if flag == "info":
                print(f"ℹ️ 日志信息: {func.__name__} 结束执行")
            elif flag == "warn":
                print(f"⚠️ 警告信息: {func.__name__} 结束执行")
                
            return result
        return wrapper
    return decorator

# 使用不同的参数装饰同一个逻辑(或不同函数)
@logging("info")
def sum_num_info(*args, **kwargs):
    return sum(args) + sum(kwargs.values())

@logging("warn")
def sum_num_warn(*args, **kwargs):
    return sum(args) + sum(kwargs.values())

sum_num_info(1, 2)
sum_num_warn(1, 2)

6. 实战案例:性能计时器

统计代码执行时间是装饰器最经典的用途之一。

import time

def timer(func):
    def wrapper():
        start = time.time()  # 记录开始时间
        func()               # 执行函数
        end = time.time()    # 记录结束时间
        print(f"⏱️ 这个函数的执行时间: {end - start:.6f} 秒")
    return wrapper

@timer
def timer_demo():
    # 模拟耗时操作
    total = 0
    for i in range(10000000):
        total += i

timer_demo()

7. 最佳实践:@wraps

在使用装饰器后,原函数的元信息(如 __name__, __doc__)会被 wrapper 覆盖。为了保持元数据,强烈建议使用 functools.wraps

from functools import wraps

def my_decorator(func):
    @wraps(func)  # 修复元数据丢失问题
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

总结

  • 闭包是基础:嵌套 + 引用 + 返回。python
  • 万能参数:内层函数永远建议使用 *args, **kwargs 以适配所有函数。
  • 返回值:不要忘记 return result
  • 带参装饰器:记住“三层嵌套”结构。