一、闭包
①定义
闭包是指一个函数(内层函数)能够“记住”并访问它所在作用域的变量(外层函数的变量),即使在外层函数已经返回的情况下。
②优缺点
优点:
- 无需定义全局变量即可实现通过函数持续地访问、修改某个值
- 闭包使用的变量的所用于在函数内,难以被错误的调用修改
缺点:
- 内部函数会持续引用外部函数的值,导致这一部分内存无法释放,一直占用内存
③基本写法
代码语言:python代码运行次数:0复制def outer_function(outer_var):
def inner_function(inner_var):
函数体
return inner_function
outer_function
:外部函数名称,负责接收一个参数并定义一个内部函数outer_var
:传递给 outer_function 的一个参数。inner_function
:在 outer_function 内部定义的函数inner_var
:传递给 inner_function 的参数
整体作用:
当调用outer_function
时,它会返回一个新的函数inner_function
,这个新的函数“记住”了outer_var
的值,inner_function
可以使用这个值与传入的inner_var
进行操作。
④nonlocal关键字
在闭包函数(内部函数中)想要修改外部函数的变量值时,需要用nonlocal
关键字声明这个外部变量。
def outer(num1):
def inner(num2):
# 声明要使用外部函数 outer 的 num1
nonlocal num1
num1= num2
print(num1)
return inner
fn=outer(10)
fn(10)
fn(10)
fn(10)
输出结果:
20
30
40
【分析】
调用 fn(10) 时,实际上是调用了 inner 函数。num1 的初始值为 10,每次调用 fn(10) 时,都会把 10 加到 num1 上,并输出更新后的值。由于 num1 是通过 nonlocal 声明的,所以每次调用都能“记住”并修改 num1 的值。
【案例】
使用全局变量account_amount记录余额的简单ATM操作:
代码语言:python代码运行次数:0复制# 通过全局变量account_amount记录余额
account_amount=0
def atm(num, deposit=True):
global account_amount
if deposit:
account_amount = num
print(f"存款: {num}, 账户余额:{account_amount}")
else:
account_amount -= num
print(f"取款:-{num}, 账户余额:{account_amount}")
atm(100)
atm(200)
atm(100, deposit=False)
运行结果:
存款: 100, 账户余额:100
存款: 200, 账户余额:300
取款:-100, 账户余额:200
【分析】
该实现简单明了,但存在全局变量带来的命名空间污染问题,容易出现变量被修改的隐患。
【改进】
通过闭包来管理ATM账户状态:
代码语言:python代码运行次数:0复制# 使用闭包实现ATM小案例
def account_create(initial_amount=0):
def atm(num, deposit=True):
nonlocal initial_amount
if deposit:
initial_amount = num
print(f"存款: {num}, 账户余额:{initial_amount}")
else:
initial_amount -= num
print(f"取款:-{num}, 账户余额:{initial_amount}")
return atm
atm = account_create()
atm(100)
atm(200)
atm(100, deposit=False)
运行结果:
存款: 100, 账户余额:100
存款: 200, 账户余额:300
取款:-100, 账户余额:200
【分析】
通过使用闭包,这段代码实现了一个简单而有效的账户系统,封装了账户余额的管理,确保了数据的安全性和操作的简便性。
二、装饰器
①定义
装饰器是一个函数,它接受另一个函数作为参数,并返回一个新的函数。这个新的函数通常会在原函数的基础上添加一些额外的功能。
本质上,装饰器也是闭包,它可在不改变目标函数的基础上,为其增加额外功能,可以看作是在函数“外面”包裹了一层新的逻辑。
②写法
写法一:闭包
定义一个闭包函数, 在闭包函数内部执行目标函数并完成功能的添加。
代码语言:python代码运行次数:0复制# 定义装饰器
def outer(func):
def inner():
print("我睡觉了")
func()
print("我起床了")
return inner
# 被装饰的函数
def sleep():
import random
import time
print("睡眠中……")
time.sleep(random.randint(1,5))
# 应用装饰器
fn=outer(sleep)
# 调用增强后的函数
fn() # 调用 fn() 实际上是调用 inner 函数
输出结果:
我睡觉了
睡眠中……
我起床了
【分析】
通过使用闭包,装饰器的使用使得sleep
函数的行为在不修改其内部实现的情况下得到了扩展,添加了额外的行为。
写法二:语法糖
使用“@”语法糖简化装饰器的定义:
代码语言:python代码运行次数:0复制# 定义装饰器
def outer(func):
def inner():
print("我睡觉了")
func()
print("我起床了")
return inner
# 使用装饰器的语法糖
@outer
def sleep():
import random
import time
print("睡眠中……")
time.sleep(random.randint(1,5))
# 调用装饰后的函数
sleep()
输出结果:
我睡觉了
睡眠中……
我起床了
【分析】
@outer
是装饰器的语法糖,等价于sleep = outer(sleep)
。其工作流程如下:
- 使用 @outer 装饰 sleep 函数时,它会把 sleep 函数传递到 outer 函数中;
- outer 返回一个 inner 函数,这个 inner 函数包含原来的 sleep 函数;
- sleep 变量现在指向 inner 函数,所以以后的所有对 sleep() 的调用实际上都在执行 inner()。