一. 闭包
1.1 什么是闭包
在一个函数中嵌套来一个函数,其中这个函数有返回值,有对数据的引用。
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。
1.2 闭包的核心的特征
- 嵌套函数:一个函数内部定义了另一个函数
- 外部函数变量:内嵌函数引用了外部函数的变量
- 返回内嵌函数:外部函数返回内嵌函数作为返回值
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. 执行流程
- my_decorator 接收 say_hello 作为参数
- 创建 wrapper 函数,包含增强逻辑
- 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
- 用途: 将用户调用时的参数完整传递给原始函数
执行时序
- 装饰阶段: greet 函数被 wrapper 替代
- 调用阶段: greet("张三") 实际调用 wrapper("张三")
- 参数转发: wrapper 将 "张三" 作为 name 参数传递给 func
- 循环执行: 重复调用 func("张三") 4次(由 num_times 控制
三. 总结
闭包的核心
- 本质:内部函数引用外部变量并返回的函数结构
- 三要素:①函数嵌套 ②引用外部变量 ③返回内嵌函数
- 关键:使用nonlocal修改外部变量,实现状态保持
装饰器核心
- 本质:基于闭包的高阶函数应用
- 作用:不改变原函数代码和调用方式,增强函数功能
- 语法:@decorator是func = decorator(func)的语法糖
核心关系
- 闭包是基础原理:实现函数的状态保存和变量封装
- 装饰器是高级应用:基于闭包实现功能增强的实际工具
- 两者是从机制到实现:闭包提供底层支持,装饰器提供优雅语法