闭包
通过全局变量account_amount来记录余额
尽管功能实现是ok的,但是仍有问题:
- 代码在命名空间上(变量定义)不够干净、整洁
- 全局变量有被修改的风险
如何解决?
- 将变量定义在函数内部是行不通的
- 我们需要使用闭包
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。
变为:
简单闭包
修改外部函数变量的值
尝试实现以atm取钱的闭包程序
# 使用闭包实现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
闭包注意事项
优点,使用闭包可以让我们得到:
- 无需定义全局变量即可实现通过函数,持续的访问、修改某个值
- 闭包使用的变量的所用于在函数内,难以被错误的调用修改
缺点:
- 由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存
总结:
1. 什么是闭包
定义双层嵌套函数, 内层函数可以访问外层函数的变量
将内存函数作为外层函数的返回,此内层函数就是闭包函数
2. 闭包的好处和缺点
- 优点:不定义全局变量,也可以让函数持续访问和修改一个外部变量
- 优点:闭包函数引用的外部变量,是外层函数的内部变量。作用域封闭难以被误操作修改
- 缺点:额外的内存占用
3. nonlocal关键字的作用
- 在闭包函数(内部函数中)想要修改外部函数的变量值
- 需要用nonlocal声明这个外部变量
装饰器
装饰器其实也是一种闭包, 其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能。
希望给sleep函数,增加一个功能:
- 在调用sleep前输出:我要睡觉了
- 在调用sleep后输出:我起床了
装饰器的一般写法(闭包写法)
定义一个闭包函数, 在闭包函数内部:
- 执行目标函数
- 并完成功能的添加
执行结果:
@outer 写法
总结:
1. 什么是装饰器
装饰器就是使用创建一个闭包函数,在闭包函数内调用目标函数。
可以达到不改动目标函数的同时,增加额外的功能。
2. 装饰器的写法
"""
演示装饰器的写法
"""
# 装饰器的一般写法(闭包)
# def outer(func):
# def inner():
# print("我睡觉了")
# func()
# print("我起床了")
#
# return inner
#
#
# def sleep():
# import random
# import time
# print("睡眠中......")
# time.sleep(random.randint(1, 5)) # 随机数 1-5
#
#
# fn = outer(sleep)
# fn()
# 装饰器的快捷写法(语法糖)
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()