Python函数:装饰器

一、装饰器的定义(什么是装饰器)

1)装饰器(Decorator)是一种特殊的函数,它的主要作用是在不修改被装饰函数的源代码和调用方式的前提下,为函数增加额外功能

2)本质:高阶函数 + 闭包。

形式表达

给定一个函数 func,装饰器 decorator 的本质是:decorator(func)→wrapper

其中,wrapper 是对 func 的包装,通常在调用时会执行额外操作,然后调用原函数 func。

逐步拆解

1)装饰器是一个函数,它接收另一个函数作为参数,比如 func

2)装饰器内部定义了一个新的函数 wrapper,这个函数会在调用时:先执行一些额外的操作(比如打印日志、权限检查等)、再调用传入的原函数 func

3)装饰器返回这个新的函数 wrapper,代替了原来的函数。

举例说明

def decorator(func):
    def wrapper():
        print("调用函数前的操作")
        func()  # 调用原函数
        print("调用函数后的操作")
    return wrapper  # 返回包装后的函数

def say_hello():
    print("Hello!")

# 使用装饰器包装函数
say_hello = decorator(say_hello)

say_hello()
"""
结果:
调用函数前的操作
Hello!
调用函数后的操作
"""

解释

1)decorator(say_hello) 返回了一个新的函数 wrapper,这个函数包裹了原来的 say_hello。

2)当你调用 say_hello() 时,实际上执行的是 wrapper(),它先打印“调用函数前的操作”,然后调用原函数 say_hello(),最后打印“调用函数后的操作”。


二、装饰器有什么用

  • 增强函数功能
    在函数执行前后添加额外操作,比如日志记录、权限校验、性能统计等。
  • 代码复用与解耦
    把通用的功能抽象成装饰器,多个函数复用,避免重复代码。
  • 函数行为的动态修改
    在运行时动态地改变函数行为,而无需修改函数本身代码。
  • 简化代码结构
    通过装饰器,代码结构更清晰,主业务逻辑和辅助功能分离。

为什么要使用装饰器

  • 避免代码重复
    例如,多个函数都需要日志打印或权限检查,用装饰器封装一次,复用多处。
  • 统一管理横切关注点
    许多功能(如缓存、权限、事务)是“横切关注点”,装饰器能集中管理,代码更整洁。
  • 提高代码可读性和维护性
    业务逻辑与辅助功能分开,代码更易于理解和维护。
  • 符合开放封闭原则
    不修改已有函数代码,扩展功能,符合软件设计原则。

方面

说明

装饰器作用

给函数动态添加功能,增强代码复用性

主要用途

日志、权限、缓存、性能统计、事务管理

使用理由

避免重复代码,分离关注点,提高维护性

不使用影响

重复代码多,重复,难维护

推荐使用场景

多函数共享的辅助功能


三、装饰器如何使用

3.1 装饰器的基本语法

装饰器本质是一个函数,接受一个函数作为参数,返回一个新的函数。

def decorator(func):
    def wrapper():
        # 在调用原函数前可以做一些操作
        print("开始执行函数")
        func()
        # 在调用原函数后可以做一些操作
        print("函数执行完毕")
    return wrapper

@decorator  # 语法糖,等同于 foo = decorator(foo)
def foo():
    print("这是被装饰的函数")

foo()
"""
输出:
复制
开始执行函数
这是被装饰的函数
函数执行完毕
"""

装饰器的工作流程

  • @decorator 把被装饰函数 foo 传给 decorator 函数
  • decorator 返回一个新函数 wrapper
  • foo 被替换成了 wrapper
  • 调用 foo() 实际调用的是 wrapper(),在其中调用原始 foo

3.2 带参数的被装饰函数

如果被装饰函数有参数,装饰器的内部函数也要支持接收参数:

def decorator(func):
    def wrapper(*args, **kwargs):
        print("开始执行函数")
        result = func(*args, **kwargs)
        print("函数执行完毕")
        return result
    return wrapper

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

print(add(3, 4))  # 输出 7

3.3 带参数的装饰器

有时装饰器本身也需要参数,这时需要三层嵌套:

def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(times=3)
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")
"""
输出:
Hello, Alice!
Hello, Alice!
Hello, Alice!
"""

3.4 链式装饰器(多个装饰器叠加)

def deco1(func):
    def wrapper():
        print("deco1 before")
        func()
        print("deco1 after")
    return wrapper

def deco2(func):
    def wrapper():
        print("deco2 before")
        func()
        print("deco2 after")
    return wrapper

@deco1
@deco2
def foo():
    print("foo")

foo()
"""
输出:
deco1 before
deco2 before
foo
deco2 after
deco1 after
"""

解释:

(1)装饰器应用顺序
Python中多个装饰器叠加时,装饰器的应用顺序是从下往上执行的。

  • foo 先被 deco2 装饰,变成 deco2(foo),返回一个新的函数(deco2 的 wrapper)。
  • 然后这个新函数又被 deco1 装饰,变成 deco1(deco2(foo)),返回最终包装后的函数(deco1 的 wrapper)。

(2)调用过程
当执行 foo() 时,实际上调用的是最外层装饰器 deco1 返回的 wrapper 函数。

(3)执行细节

  • deco1 的 wrapper 首先打印 "deco1 before"。
  • 然后调用它内部的 func(),此时 func 是 deco2(foo) 返回的 wrapper 函数。
  • 进入 deco2 的 wrapper,打印 "deco2 before"。
  • 调用原始的 foo() 函数,打印 "foo"。
  • deco2 的 wrapper 打印 "deco2 after",执行结束返回。
  • 回到 deco1 的 wrapper,打印 "deco1 after",执行结束返回。

(4)总结

  • 装饰器的嵌套调用使得输出顺序呈现“先外后内,后内先出”的特点。
  • 也就是说,装饰器的“before”部分按装饰器叠加顺序从外到内依次执行,“after”部分则按相反顺序执行。

四、易错点分析

4.1 装饰器不支持带参数的函数(忘写*args, **kwargs

错误示例:

def decorator(func):
    def wrapper():
        print("开始执行")
        func()  # 如果被装饰函数有参数,这里会报错
    return wrapper

@decorator
def foo(name):
    print(f"Hello, {name}")

foo("Alice")  # TypeError: wrapper() takes 0 positional arguments but 1 was given

正确写法:

def decorator(func):
    def wrapper(*args, **kwargs):
        print("开始执行")
        return func(*args, **kwargs)
    return wrapper

4.2 装饰器顺序错误导致逻辑混乱(链式装饰器)

多个装饰器叠加时,装饰器的应用顺序和调用顺序容易混淆。

  • 装饰器从下往上应用。
  • 调用时从最外层装饰器开始。

4.3 装饰器中忘记返回函数结果

包装函数如果没有返回被装饰函数的返回值,会导致调用者拿不到结果。

def decorator(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)  # 忘记return
    return wrapper

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

print(add(1, 2))  # 输出 None,而不是 3

正确写法:

def decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

4.4 带参数装饰器忘记多层嵌套

带参数的装饰器必须三层函数嵌套,否则参数无法传递

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值