Python装饰器:高效代码增强利器

Python基础:装饰器

一、知识点详解

1. 装饰器是什么?

  • 本质定义
    装饰器是接收函数并返回新函数的高阶函数。
    它能够通过 @语法糖在不改变原函数代码的前提下,给原函数添加新的功能。

  • 核心原理
    利用闭包(内层函数引用外层函数变量)和函数作为对象(一等公民)的特性,实现“包装器”逻辑。

  • 语法糖本质
    “语法糖”是一种编程特性,它能将某些代码操作以简洁且优雅的形式呈现。
    例如,在装饰器使用场景中,@decorator 作为 func = decorator(func) 的简写形式,
    通过直接在函数定义上方使用 @ 符号,就能便捷地绑定装饰器,这里的 @ 便是语法糖的体现。
    --
    此外,Python 里还有不少语法糖,
    [x for x in range(10)] 这样的 推导式(用于列表、字典等快速生成)、
    a if condition else b 这种 条件表达式(即三元运算符),
    以及 if (n:=len(data)) > 0: 中的 海象运算符:=),可在条件判断中同时完成赋值操作等。


2. 为什么使用装饰器

使用装饰器主要有以下好处:

  • 代码复用:多个函数需要相同额外功能时,封装成装饰器避免代码重复。

  • 代码简洁:分离额外功能与核心逻辑,原函数专注于核心功能的实现。

  • 易于维护:修改额外功能时只需调整装饰器,无需逐个修改被装饰函数。

3. 装饰器怎样使用

装饰器使用“三步走”

  1. 定义装饰器函数
    接收目标函数为参数,返回新的包装函数。
  2. 定义原函数
    编写需增强功能的核心函数。
  3. 应用装饰器
    在原函数定义前使用 @装饰器名,等价于 原函数 = 装饰器(原函数)

4. 应用场景

  • 计时:记录函数执行时间(使用 time.time() 计算时间差)。

  • 权限校验:根据参数校验用户角色,抛出异常(如 PermissionError)。

  • 类型检查:通过函数注解 @enforce_types 验证参数类型(利用 func.__annotations__)。

  • 返回值增强:修改原函数返回值(如自动加固定值)。

5. 常见错误

  1. 错误调用函数@decorator 不是 @decorator(),装饰器直接绑定函数而非调用
  2. 参数传递错误:未使用*args, **kwargs导致参数丢失,需在包装函数中兼容任意参数
  3. 循环导入:装饰器和被装饰函数相互引用时拆分代码,避免模块间循环依赖

二、说明示例

1. 无参数装饰器 (基本用法)

通过闭包定义内层包装函数,在调用原函数前后添加逻辑(如计时),返回包装函数。

import time 
   
def timer(func):  
    def wrapper():  
        start = time.time()  # 记录开始时间
        result = func()       # 执行原函数
        print(f"耗时:{time.time()-start:.2f}秒")  # 计算耗时
        return result         # 显式返回原函数结果
    return wrapper  

@timer  
def slow_func():  
    time.sleep(1)  

slow_func()  # 输出执行时间  

2. 保留原函数元信息

使用 functools.wraps 装饰包装函数,避免原函数的名称、文档字符串等元信息被覆盖。

from functools import wraps  

def decorator(func):  
    @wraps(func)  # 保留原函数元信息(函数名、注释等)  
    def wrapper(*args, **kwargs):  
        return func(*args, **kwargs)  
    return wrapper  

@decorator  
def example():  
    """示例函数"""  
    pass  

print(example.__name__)  # 输出"example"(而非默认的"wrapper")  

3. 处理带参数的函数

包装函数使用 *args, **kwargs 接收任意参数,确保装饰器兼容不同参数形式的函数。

from functools import wraps  

def logger(func):  
    @wraps(func)  # 保留原函数元信息(如函数名、文档字符串)  
    def wrapper(*args, **kwargs):  
        print(f"调用函数:{func.__name__},参数:{args}, {kwargs}")  
        return func(*args, **kwargs)  # 传递参数并返回原函数结果  
    return wrapper  

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

print(add(2, b=3))  # 输出参数和结果  

4. 带参数的装饰器

通过三层嵌套函数,外层接收装饰器参数,中层绑定目标函数,内层包装函数实现参数化逻辑(如重复执行函数n次)。

from functools import wraps

def repeat(n):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            results = []
            # 循环n次调用原函数
            for _ in range(n):
                results.append(func(*args, **kwargs))
            return results  # 返回最终结果而非None
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    print(f"Hello {name}")  # 打印欢迎语句
    return f"Hello {name}"  # 返回欢迎语句


greet("小帅")  # 输出3次"Hello 小帅"
print(greet("小帅"))  # ['Hello 小帅', 'Hello 小帅', 'Hello 小帅']

三、知识点总结

  1. 装饰器的本质与核心原理
    装饰器是接收函数并返回新函数的高阶函数,利用闭包和函数作为“一等公民”的特性,在不修改原函数代码的前提下添加新功能。

  2. 语法糖的本质
    @decoratorfunc = decorator(func) 的简写,通过简洁符号实现装饰器与函数的绑定,类似Python中推导式、三元运算符等简化代码的语法特性。

  3. 使用装饰器的优势
    通过封装通用功能(如计时、日志)实现代码复用,分离核心逻辑与额外功能,提升代码简洁性和可维护性。

  4. 装饰器的使用步骤
    定义接收目标函数并返回包装函数的装饰器 → 编写需增强的原函数 → 用 @装饰器名 绑定(等价于函数重赋值)。


四、扩展知识

4.1 多层装饰器

def decorator1(func):
    def wrapper1(*args, **kwargs):
        print("进入外层装饰器 wrapper1")
        print("校验权限...")
        # 这里可加入权限校验逻辑
        result = func(*args, **kwargs)  # 调用 decorator2 的 wrapper2
        print("离开外层装饰器 wrapper1")
        return result

    return wrapper1


def decorator2(func):
    def wrapper2(*args, **kwargs):
        print("进入内层装饰器 wrapper2")
        result = func(*args, **kwargs)  # 调用原始函数
        print("记录操作日志...")
        # 这里可加入记录操作逻辑
        print("离开内层装饰器 wrapper2")
        return result

    return wrapper2


@decorator1
@decorator2
def my_func():
    print("执行原始函数")


# 调用被装饰后的函数
my_func()

执行流程说明

  1. 装饰器绑定:
    my_func = decorator1(decorator2(my_func))
  1. 调用 my_func() 时的实际执行顺序:
    • 进入 decorator1wrapper1
    • wrapper1 中执行权限校验逻辑
    • wrapper1 调用 decorator2wrapper2
    • wrapper2 调用原始函数 my_func
    • wrapper2 中执行日志记录逻辑
    • 以上执行完毕后,以相反顺序返回
    • 离开 decorator2wrapper2
    • 离开 decorator1wrapper1

输出结果

进入外层装饰器 wrapper1
校验权限...
进入内层装饰器 wrapper2
执行原始函数
记录操作日志...        
离开内层装饰器 wrapper2
离开外层装饰器 wrapper1

总结
多层装饰器按 @外层 @内层 顺序绑定,调用时从外层包装函数向内层执行,最终调用原函数(等价于嵌套函数调用)。

4.2 装饰器函数的执行时机

装饰器函数在模块加载时立即执行(仅执行一次),用于绑定被装饰函数,而 wrapper 函数在每次调用被装饰函数时执行。

def deco1(func):
    print("装饰器1初始化")  # 绑定阶段第二步:(绑定顺序自下而上)
    def wrapper1():
        print("执行装饰器1逻辑")  # 调用阶段第一步:(调用顺序自上而下)
        return func()  # 调用内层装饰器的wrapper2
    return wrapper1

def deco2(func):
    print("装饰器2初始化")  # 绑定阶段第一步:(绑定顺序自下而上)
    def wrapper2():
        print("执行装饰器2逻辑")  # 调用阶段第二步:(调用顺序自上而下)
        return func()  # 调用原始函数target
    return wrapper2

@deco1  # 等价于 target = deco1(deco2(target))
@deco2  # 先执行deco2(target),再将结果传给deco1
def target():
    print("执行目标函数")  # 调用阶段第三步:(调用顺序自上而下)

target()  # 触发装饰器链的执行

以上代码执行流程

阶段 执行步骤 输出结果
绑定阶段 执行deco2(target) 打印"装饰器2初始化"
执行deco1(wrapper2) 打印"装饰器1初始化"
调用阶段 执行wrapper1() 打印"执行装饰器1逻辑"
执行wrapper2() 打印"执行装饰器2逻辑"
执行target() 打印"执行目标函数"

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容