闭包与装饰器

一. 闭包

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)的语法糖

核心关系

  • 闭包是基础原理:实现函数的状态保存和变量封装
  • 装饰器是高级应用:基于闭包实现功能增强的实际工具
  • 两者是从机制到实现:闭包提供底层支持,装饰器提供优雅语法
相关推荐
fyzy2 小时前
C++写后端实现,实现前后端分离
开发语言·c++
全栈测试笔记2 小时前
异步函数与异步生成器
linux·服务器·前端·数据库·python
huohuopro2 小时前
Mybatis的七种传参方式
java·开发语言·mybatis
Lee_SmallNorth2 小时前
变态需求之【角色不同访问数据库的用户不同】
java·开发语言·数据库
扶苏-su2 小时前
Java网络编程:InetAddress 详解
java·开发语言·网络
木头左2 小时前
基于Backtrader框架的指数期权备兑策略实现与分析
python
素心如月桠2 小时前
cmd 输入 python --version 输出为空(windows11系统安装python后执行python --version没反应)
python
飞Link3 小时前
深度解析 HyperLPR:高性能中文车牌识别框架从入门到实战
python
IOT-Power3 小时前
QT构建构建DataBus总线
开发语言·qt