欢迎光临
我们一直在努力

6.python中的闭包和装饰器

目录

      • 一、 闭包的概念
      • 二、 闭包的形成条件
      • 三、 闭包的代码示例
        • 1. 基本闭包示例
        • 2. 带参数的闭包示例
        • 3. 修改外部变量(nonlocal 关键字)
      • 四、 闭包的作用与应用场景
        • 详细代码示例
        • 装饰器执行流程拆解
        • 为什么说装饰器必须是闭包?
      • 五、 进阶:带参数的装饰器(三层闭包)
        • 逻辑构造
        • 运行原理拆解
      • 六、 装饰器的叠加顺序(套娃顺序)
        • 代码示例
        • 深度解析
      • 七、 闭包与装饰器的优缺点总结
      • 八、 总结

一、 闭包的概念

闭包(Closure) 是指一个函数可以访问并记住其词法作用域中的变量,即使该函数在其词法作用域之外执行。闭包由函数和其引用的外部变量组合而成,常用于实现数据封装或延迟计算。

二、 闭包的形成条件

  • 函数嵌套:一个函数内部定义了另一个函数。
  • 环境引用:内部函数引用了外部函数的变量(局部变量或参数)。
  • 返回内部函数:外部函数返回内部函数对象,使其能在外部环境被调用。

  • 三、 闭包的代码示例

    1. 基本闭包示例

    简单理解:函数的调用本质上就是“函数地址 + 括号”。outer 函数返回了 inner 的地址。调用 outer 后,用 ou 存储了 inner 的地址,调用 ou() 实际上就是在执行 inner 函数。

    def outer():
    a = 10
    def inner():
    print(a) # 引用了外部变量 a
    return inner

    ou = outer()
    ou() # 输出: 10

    2. 带参数的闭包示例

    闭包不仅可以访问外部函数定义的局部变量,还可以访问外部函数的参数。

    def multiplier(factor):
    # 内部函数使用了外部函数的参数 factor
    def multiply(number):
    return number * factor
    return multiply

    # 创建一个乘以 3 的函数
    times_three = multiplier(3)
    # 创建一个乘以 5 的函数
    times_five = multiplier(5)

    print(times_three(10)) # 输出: 30
    print(times_five(10)) # 输出: 50

    3. 修改外部变量(nonlocal 关键字)

    在 Python 中,如果内部函数需要修改外部不可变类型变量(如整数、字符串),必须使用 nonlocal 声明,否则会被视为定义了一个新的局部变量。

    def create_counter():
    count = 0
    def increment():
    nonlocal count # 声明 count 是外部函数的变量
    count += 1
    return count
    return increment

    counter = create_counter()
    print(counter()) # 输出: 1
    print(counter()) # 输出: 2


    四、 闭包的作用与应用场景

  • 实现数据封装和隐藏 通过闭包,可以将数据隐藏在外部函数的局部作用域中,外部无法直接访问,只能通过暴露出的内部函数接口进行操作。

  • 保持状态(延迟计算) 普通函数执行完后局部变量会销毁。而闭包由于内部函数持有引用,延长了变量的生命周期,可以在多次调用间保持状态。

  • 实现装饰器(Decorator)—— 闭包的高级应用 装饰器是 Python 中最能体现闭包价值的场景。本质上,装饰器就是一个返回函数的高阶函数。

  • 详细代码示例

    def log_decorator(func):
    # func 是外部函数的参数,被内部 wrapper 闭包捕获
    def wrapper(*args, **kwargs):
    """
    *args 和 **kwargs 保证了无论原函数有多少参数,
    装饰器都能原封不动地进行参数转发。
    """

    print(f"【日志】即将调用函数: {func.__name__}")

    # 真正执行原函数逻辑
    result = func(*args, **kwargs)

    print(f"【日志】函数 {func.__name__} 调用完成")
    return result

    return wrapper

    @log_decorator
    def say_hello(name):
    print(f"Hello, {name}!")

    say_hello("Alice")

    装饰器执行流程拆解

    当我们使用 @log_decorator 语法糖时,后台执行逻辑如下:

    • 闭包形成:log_decorator 接收 say_hello 函数对象作为参数。
    • 环境捕获:wrapper 引用了 func,并将其保存在自己的闭包环境中。
    • 函数替换:等同于执行了 say_hello = log_decorator(say_hello)。
    • 延迟执行:调用 say_hello("Alice") 时,实际上是在调用 wrapper("Alice")。
    为什么说装饰器必须是闭包?

    如果 wrapper 无法记住 func 变量,那么在外部调用时就找不到原本的函数逻辑。闭包确保了即便 log_decorator 执行结束,func 依然存活。

    小贴士:建议使用 from functools import wraps 并在 wrapper 上添加 @wraps(func),以保留原函数的元数据(如 __name__)。



    五、 进阶:带参数的装饰器(三层闭包)

    有时候我们需要装饰器本身能接收参数(例如:指定重复执行次数)。这时就需要用到三层嵌套闭包。

    逻辑构造
  • 第一层(外部配置):接收装饰器本身的参数。
  • 第二层(标准装饰):接收被装饰的函数对象 func。
  • 第三层(最终执行):接收原函数运行时的参数 *args, **kwargs。
  • def repeat(times):
    def decorator(func):
    def wrapper(*args, **kwargs):
    result = None
    for i in range(times):
    print(f"— 第 {i+1} 次执行 —")
    result = func(*args, **kwargs)
    return result
    return wrapper
    return decorator

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

    greet("Python")

    运行原理拆解

    当你写下 @repeat(3) 时,Python 实际上在做“套娃”式的接力:

    • Step 1: 先执行 repeat(3),返回中间层 decorator 的地址。
    • Step 2: Python 自动调用 decorator(greet),返回最里层 wrapper 的地址。
    • Step 3: 最终执行 greet("Python") 时,本质是在执行 wrapper("Python")。

    六、 装饰器的叠加顺序(套娃顺序)

    当一个函数同时被多个装饰器装饰时,执行顺序遵循**“从近到远封装,从外到内执行”**的原则。

    代码示例

    def bold(func):
    def wrapper():
    return f"<b>{func()}</b>"
    return wrapper

    def italic(func):
    def wrapper():
    return f"<i>{func()}</i>"
    return wrapper

    @bold
    @italic
    def hello():
    return "Hello World"

    print(hello())
    # 输出结果:<b><i>Hello World</i></b>

    深度解析

    你可以把装饰器想象成给礼物打包的过程:

  • 靠近函数的先打包:@italic 离 hello 最近,它先给函数套上一层 <i>。
  • 远离函数的后打包:@bold 在外面,它给已经套了 <i> 的整体再套上一层 <b>。
  • 执行流程图: 调用 hello()

    \\rightarrow

    进入 bold.wrapper

    \\rightarrow

    进入 italic.wrapper

    \\rightarrow

    执行原函数 hello()

    \\rightarrow

    返回 italic 结果

    \\rightarrow

    返回 bold 结果。


    七、 闭包与装饰器的优缺点总结

    维度说明
    优点 减少全局变量使用,避免污染;代码结构紧凑,比定义类更轻量;解耦逻辑,实现无侵入式的代码增强。
    缺点 内存消耗:外部变量驻留在内存中,直到闭包销毁;调试困难:多层嵌套后,报错追踪栈会变得复杂;循环绑定问题:需注意延迟绑定现象。

    八、 总结

    闭包是 Python 中非常重要且强大的特性。它打破了常规的作用域生命周期规则,使得函数可以携带“环境状态”。从基础的变量记住,到复杂的装饰器三层嵌套,理解了**“地址接力”和“环境捕获”**,你就真正掌握了闭包的精髓。

    赞(0)
    未经允许不得转载:171主机测评 » 6.python中的闭包和装饰器
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址