闭包与装饰器

一. 闭包

1.1 什么是闭包

在一个函数中嵌套来一个函数,其中这个函数有返回值,有对数据的引用。

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。

1.2 闭包的核心的特征

  1. 嵌套函数:一个函数内部定义了另一个函数
  2. 外部函数变量:内嵌函数引用了外部函数的变量
  3. 返回内嵌函数:外部函数返回内嵌函数作为返回值

1.3 示例

python 复制代码
def outer_function(msg):
    message = msg  # 外部函数的变量

    def inner_function():  # 嵌套函数
        print(message)  # 引用外部函数的变量

    return inner_function  # 返回嵌套函数


func = outer_function("Hello, Closure!")    
func()  

输出结果:

Hello, Closure!

1.4 如何在闭包中对局部作用域进行更改

使用nonlocal关键字

nonlocal是 Python 中的一个关键字,用于在嵌套函数中声明一个变量不是局部变量,而是来自外层函数的变量。它允许在嵌套函数中修改外层函数的变量值。

python 复制代码
def get_home():
    a = 20  # 外部函数的局部变量
    def inner():
        nonlocal a  # nonlocal:引用外部函数的变量,允许修改外部函数的局部变量
        a = 30      # 修改外部函数的变量a的值
    
    print(1,a)      # 打印修改前的值,此时a=20
    inner()         # 调用内部函数,执行变量修改操作
    print(2,a)      # 打印修改后的值,此时a=30

get_home()          # 调用外部函数,执行整个闭包过程

与global的区别

nonlocal 用于嵌套函数中,指定变量来自外层函数的局部作用域。global 用于指定变量来自全局作用域。两者都用于修改非当前作用域的变量

二. 装饰器

2.1 什么是装饰器

在不改变现有函数源代码以及函数调用方式的前提下,实现给函数增加额外的功能。

装饰器的本质就是一个闭包函数(三步:① 有嵌套 ② 有引用 ③ 有返回)

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

2.2 装饰器基本概念

装饰器使用@符号作为语法糖,通常放在函数定义的前面。

例如:

python 复制代码
@decorator
def function():
    pass

这等同于:

python 复制代码
def function():
    pass
function = decorator(function)

2.3 装饰器的实现方式

2.3.1 函数装饰器

被装饰函数无参数

函数角色定义:
1. 装饰器(my_decorator) 函数
  • 装饰器函数:接收被装饰函数作为参数
  • 返回包装函数:将原函数替换为增强版本
  • 参数传递:将原函数 func 传递给内部包装函数
2. 包装(wrapper) 函数
  • 包装函数:实际执行被装饰函数的增强逻辑
  • 前置操作:在原函数执行前添加额外功能
  • 后置操作:在原函数执行后添加额外功能
  • 函数调用:执行原始的 func 函数
3. 被装饰(say_hello) 函数
  • 被装饰函数:原始业务逻辑函数
  • 被替换:通过 @my_decorator 装饰器被 wrapper 函数替代
  • 功能增强:获得装饰器添加的前置和后置操作
4. 执行流程
  1. my_decorator 接收 say_hello 作为参数
  2. 创建 wrapper 函数,包含增强逻辑
  3. say_hello 被 wrapper 替代,实现功能增强
python 复制代码
def my_decorator(func):
    def wrapper():
        print("在函数执行前做一些事情")
        func()
        print("在函数执行后做一些事情")
    return wrapper

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

2.3.2 带参数的装饰器

python 复制代码
def repeat(num_times):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator_repeat

@repeat(num_times=4)
def greet(name):
    print(f"Hello {name}")

greet("张三")    # 输出四遍 "Hello 张三"
函数角色定义:
1. repeat 函数
  • 参数: num_times - 控制重复执行次数
  • 角色: 装饰器工厂函数,接收参数并返回实际的装饰器
  • 返回: decorator_repeat 函数
2. decorator 函数
  • 参数: func - 被装饰的目标函数
  • 角色: 实际的装饰器函数,接收被装饰函数作为参数
  • 返回: wrapper 包装函数
3. wrapper 函数
  • 参数: *args, **kwargs - 接收被装饰函数的所有参数
  • 角色: 执行逻辑的包装函数,实现重复调用功能
  • 返回: 最后一次函数执行的结果
4. greet 函数
  • 装饰: 通过 @repeat(num_times=4) 装饰器修饰
  • 功能: 打印问候信息
  • 实际执行: 被 wrapper 函数替代
5. 执行流程

装饰阶段

  • repeat(num_times=4) 被调用
  • 返回 decorator_repeat 函数
  • decorator_repeat 装饰 greet 函数
  • greet 变量指向 wrapper 函数

执行阶段

  • repeat(num_times=4) 提供重复次数参数
  • decorator_repeat 包装 greet 函数
  • wrapper 实现循环执行逻辑
  • 最终 greet("张三") 实际执行4次

关系总结

  • repeat → 返回 → decorator_repeat

repeat 函数是装饰器工厂,它接收参数 num_times 并返回真正的装饰器函数decorator_repeat

  • decorator_repeat → 返回 → wrapper

decorator_repeat 函数是实际的装饰器,它接收被装饰函数 func 并返回包装函数 wrapper

  • wrapper → 替代 → greet

wrapper 函数是执行体,它实现了重复调用逻辑,最终替代了原始的 greet 函数

参数传递流程
1. num_times 参数传递
  • 来源: 外层函数 repeat(num_times=4) 调用时传入
  • 流向: 通过闭包机制传递给内层 wrapper 函数
  • 用途: 控制循环执行次数 (for _ in range(num_times))
2. fuc 参数传递
  • 来源: 中间层函数 decorator_repeat(func) 接收被装饰函数
  • 流向: 传递给内层 wrapper 函数
  • 用途: 在循环中执行原始函数 (func(*args, **kwargs))
3. *args,**kwargs 参数传递
  • 来源: 被装饰函数的实际调用参数 (greet("张三"))
  • 流向: 从 wrapper 函数传递给原函数 func
  • 用途: 将用户调用时的参数完整传递给原始函数
执行时序
  1. 装饰阶段: greet 函数被 wrapper 替代
  2. 调用阶段: greet("张三") 实际调用 wrapper("张三")
  3. 参数转发: wrapper 将 "张三" 作为 name 参数传递给 func
  4. 循环执行: 重复调用 func("张三") 4次(由 num_times 控制

三. 总结

闭包的核心

  • 本质:内部函数引用外部变量并返回的函数结构
  • 三要素:①函数嵌套 ②引用外部变量 ③返回内嵌函数
  • 关键:使用nonlocal修改外部变量,实现状态保持

装饰器核心

  • 本质:基于闭包的高阶函数应用
  • 作用:不改变原函数代码和调用方式,增强函数功能
  • 语法:@decorator是func = decorator(func)的语法糖

核心关系

  • 闭包是基础原理:实现函数的状态保存和变量封装
  • 装饰器是高级应用:基于闭包实现功能增强的实际工具
  • 两者是从机制到实现:闭包提供底层支持,装饰器提供优雅语法
相关推荐
喵手2 分钟前
Python爬虫实战:地图 POI + 行政区反查实战 - 商圈热力数据准备完整方案(附CSV导出 + SQLite持久化存储)!
爬虫·python·爬虫实战·零基础python爬虫教学·地区poi·行政区反查·商圈热力数据采集
熊猫_豆豆7 分钟前
YOLOP车道检测
人工智能·python·算法
nimadan129 分钟前
**热门短剧小说扫榜工具2025推荐,精准捕捉爆款趋势与流量
人工智能·python
杜子不疼.11 分钟前
PyPTO:面向NPU的高效并行张量编程范式
开发语言
lly20240612 分钟前
C# 结构体(Struct)
开发语言
默默前行的虫虫13 分钟前
MQTT.fx实际操作
python
YMWM_23 分钟前
python3继承使用
开发语言·python
JMchen12324 分钟前
AI编程与软件工程的学科融合:构建新一代智能驱动开发方法学
驱动开发·python·软件工程·ai编程
Once_day38 分钟前
C++之《程序员自我修养》读书总结(1)
c语言·开发语言·c++·程序员自我修养