上一节, 我们学习完了变量作用域, 以及关键字, 掌握了嵌套函数修改变量的办法。本节课, 解锁函数式编程核心知识点, 也就是闭包, 它是装饰器的前置基础, 日常用于做计数、参数固化、简易数据封存都很不错, 也是面试高频考点, 零基础的话跟着案例一步步去理解就行。
一、闭包是什么?先记3个必备条件
闭包属于嵌套函数的一种特殊形态, 简单来说, 当外层函数运行告一段便后, 内层函数仍然能够记住并且调用外层函数所定义的变量, 甚而外围函数已然执行完毕进而退出。
想要构成合法闭包,必须同时满足3点:
1. 存在嵌套结构:外层函数内部定义内层函数
2. 外层函数之中存在局部变量, 并非全局变量, 而内层函数会对其进行引用, 此局部变量也就是自由变量。
3. 外层函数把内层函数本身作为返回值传出
常规嵌套对比(非闭包)
普通嵌套只在外层内部调用内层,不返回内层,不算闭包:
def outer():
num = 10
def inner():
print(num)
inner()
outer()
那局部变量 num , 在外层执行完之后, 马上就被销毁掉, 而外部, 是没有办法去调用 inner 的, 所以, 很明显它是不满足闭包条件。
二、最简闭包示例,直观感受特性
基础写法
def outer(num):
# 外层自由变量
def inner():
print(f"获取外层数值:{num}")
# 返回内层函数,不加括号(返回函数对象而非执行结果)
inner
# 接收返回的内层函数,outer执行完毕结束运行
func = outer(28)
# 外层早已结束,内层依旧能读取外层变量,闭包生效
func()
运行结果:
获取外层数值:28
核心亮点在于, outer(28) 执行完毕, 本应被销毁的局部变量, 因闭包保留了变量环境, 使得 inner 能够持续进行访问。
多次调用独立封存数据
每次调用外层函数,会生成一组独立的变量环境,互不干扰:
def outer(num):
def inner():
print(num)
inner
f1 = outer(5)
f2 = outer(99)
f1()
f2()
输出:
99
两套独立环境,数据隔离,不用定义全局变量就能存状态。
三、闭包修改变量:搭配使用
要是打算于内层之中对外层自由变量予以修改, 继续采用前边学到的关键字就行, 适宜作为累加计数的那种场景。
def ():
cnt = 0
def ():
cnt
cnt += 1
print(f"当前计数:{cnt}")
= ()
()
()
()
运行结果:
当前计数:1
当前计数:2
当前计数:3
优势点在于, 计数变量 cnt 是通过闭包予以封存的, 在全局区域是没法访问到的, 如此便能避免全局变量被任意地篡改, 达成简易形式的数据隐藏。
四、闭包常见实用场景
场景1:固定基础参数,简化重复调用
比如统一设置税率,多次计算不同商品税后价格:
def (rate):
def calc(price):
price * (1 + rate)
calc
# 创建增值税13%计算器
tax13 = (0.13)
print(tax13(100))
print(tax13(200))
不用每次重复传入税率,参数提前固化,代码更简洁。
场景2:小型状态计数器
前面那些累加起来的案例堪称典型, 用来做访问次数的统计, 进行打卡计数尤为便利, 相较于全局变量显得更为安全。
场景3:装饰器前置铺垫
接下去要学习的装饰器, 其本质实际上是闭包的进一步应用, 闭包起着保留原函数对象、拓展功能的作用, 当下要是把原理彻底弄明白, 那么下一节入门装饰器的时候就会轻松许多。
五、新手易错避坑总结
1. 返回函数别加括号
单独的“inner”正确, 当出现“inner()”时, 它会直接去执行内层部分, 并且返回执行出来的结果, 然而却没办法形成闭包。
2. 修改外层变量必须用
仅仅读取外层的变量, 却不使用关键字, 在进行赋值修改的时候, 不加以添加, 这样的情况下, 就会创建出内层的局部变量, 进而导致闭包失去作用。
3. 不要滥用闭包
先是简单逻辑为优先于普通函数的存在;待到有长期留存少量状态、隔离数据的需求之时, 才运用闭包;复杂状态管理则将优先采用面向对象类的写法, 如此这般。
4. 自由变量常驻内存
闭包会持续持有外层变量,不用时及时释放引用,减少内存占用。
六、课堂综合练习
依据要求撰, 闭包来达成, 折扣计价这一操作, 外层将固定折扣予以传入, 内层把原价接收, 进而输出其折后价。
参考代码:
def ():
def ():
*
# 8折计价工具
sale8 = (0.8)
print(sale8(200))
本节要点复盘
1. 存在闭包的三个要素, 其一为嵌套函数, 其二是内层引用外层的变量, 其三是外层返回内层函数对象。
2. 核心优势在于, 外层函数结束之时, 内层会保留变量所处的环境, 将状态予以封存, 把数据进行隔离, 以此来替代全局变量。
3. 修改变量搭配 ,返回函数禁止添加执行括号
4. 常用场景:计数、固定参数工具、装饰器底层基础