你可曾有过好奇, , 那当中优雅的@语法背后隐匿着怎样的逻辑;要是你运用@timer去装饰函数就能够自动进行运行时间统计, 借由@便能轻而实施权限校验之际, 实际上都是闭包以及装饰器在悄然运作了;这俩看起来抽象的概念, 正是代码简洁与灵活的秘密武器;今儿个, 我们要从原理一直到实践, 一步步去揭开它们的面纱了。
1. 闭包:函数里的"状态容器"
1.1 闭包的定义与形成条件
闭包属于函数式编程里的核心特性, 归根结底是“嵌套函数、自由变量以及作用域延伸”组成的结合体。一旦内层的函数引用了处于外层函数作用范围里的变量, 并且外层函数将内层函数返回, 如此便形成了闭包。
形成闭包必须满足三个条件:
代码示例:最简单的闭包实现
def ():
# 外层函数变量(自由变量)
info =
def ():
# 内层函数引用外层自由变量
print(f"接收到消息:{info}")
# 返回内层函数对象(不执行)
10
# 创建闭包实例:固定自由变量info为"Hello "
11
= ("Hello ")
12
# 调用闭包,此时外层函数已执行完毕,但info仍被保存
13
() # 输出:接收到消息:Hello
1.2 闭包的工作原理:自由变量的"保鲜术"
闭包最为关键的神奇魔力主要在于能够延长自由变量的生命周期, 在正常状况情形下, 当函数执行完毕之后, 其内部所包含的变量会被进行垃圾回收操作, 然而闭包依靠属性, 把自由变量保存于特殊的cell对象里边, 致使内层函数就算脱离了外层函数的作用域范围, 依旧能够访问这些变量。
代码示例:探索闭包的内部状态
def ():
count = 0 # 自由变量:被闭包保存的计数器
def ():
count # 声明修改外层变量( 3+特性)
count += 1
count
# 创建计数器闭包
10
= ()
11
print(()) # 输出:1
12
通过执行print(())这一操作, 其输出结果为2, 此结果表明count值处于被持续保存的状态。
13
14
# 查看闭包保存的自由变量
15
print(.) # 输出:(,)
16
进行print(..)操作, 其输出结果为2(此数字是通过直接访问cell里的count值得出)。
1.3 闭包的实用场景:简洁的状态管理
解决“需要携带状态但不想用类”这一情景状况, 闭包最为擅长, 其常见应用涵盖以下方面:
代码示例:温度转换器工厂
def r(unit):
"""根据单位生成不同的温度转换函数"""
def t(c):
c * 1.8 + 32
def (c):
c + 273.15
# 根据外层参数返回不同的内层函数
if unit == "":
t
10
elif unit == "":
11
12
13
# 创建不同转换器(闭包实例)
14
to_f = r("")
15
to_k = r("")
16
17
打印, 将零进行转换为华氏度的操作之后的结果, 此结果为三十二点零, 也就是零摄氏度所对应的华氏度数值。
18
打印, 将零摄氏度转换为开尔文的结果, 输出为二百七十三点一五, 即print(to_k(0)) # 输出: 273.15(0℃转开尔文)。
2. 装饰器:函数的"增强插件"
2.1 装饰器的本质与语法糖
闭包的典型应用之中有装饰器, 其本质是这样一种高阶函数, 即接受函数当作参数, 并且返回增强版函数, 借助@装饰器名语法糖, 装饰器的使用得以简化, 进而让代码变得更加简洁。
核心逻辑是, @等价于func = (func), 这意味着, 要将被装饰函数替换成, 装饰器返回的新函数。
代码示例:无参装饰器基础实现
def (func):
"""打印函数调用日志的装饰器"""
def ():
print(f" 函数 {func.} 开始执行")
func() # 调用原函数
print(f" 函数 {func.} 执行完毕")
# 使用@语法糖应用装饰器
10
@
11
def ():
12
print("Hello !")
13
14
# 调用被装饰后的函数
15
()
16
# 输出:
17
# 函数 开始执行
18
# Hello !
19
# 函数 执行完毕
2.2 基础装饰器的实现步骤
实现一个装饰器通常需要3步:
定义装饰函数:接受被装饰函数func作为参数;
自定义一个用于包装的函数, 在这个函数里面进行对func的调用, 并且添加诸如日志记录、计时操作以及权限校验之类的增强逻辑。
返回包装函数:将作为结果返回,替换原函数。
代码示例:计时装饰器(带参数与返回值处理)
time
def (func):
def (*args, **):
"""包装函数:支持任意参数和返回值"""
= time.time()
对原函数展开调用, 同时, 将其返回值予以捕获, 具体形式为 func(*args, **)。
= time.time()
你提供的内容存在错误信息并有缺失部分, 不太能准确按照要求改写。请你检查并修正完整准确的内容后再次提出。
10
# 返回原函数的结果
11
12
13
@
14
def (n):
15
"""计算1到n的和"""
16
sum(range(1, n+1))
17
18
# 调用被装饰的函数
19
print(()) # 输出:
20
于此同时进行打印: 进行执行操作所耗费的时间为: 零点零零二三秒(此时间会依据设备不同而有所变化)。
2.3 进阶技巧:带参数的装饰器与装饰器链
要是装饰器得自定义参数, 像日志级别、权限角色这类, 那就得用三层嵌套, 外层接收参数, 中层接收函数, 内层是包装函数。而且, 能把多个装饰器叠加一块用, 也就是装饰器链, 执行顺序是从下往上, 靠近函数的装饰器先运行。
代码示例1:带参数的日志装饰器
def (level):
"""带日志级别参数的装饰器(三层嵌套)"""
def (func):
def ():print(f"
{level.upper()}
函数 {func.} 被调用")func()
# 应用带参数的装饰器
@("")
10
def ():
11
print("执行核心操作...")
12
13
()
14
# 输出: 函数 被调用
15
# 执行核心操作...
代码示例2:装饰器链的执行顺序
def (func):
def ():
print("装饰器A:开始")
func()
print("装饰器A:结束")
def (func):
def ():
10
print("装饰器B:开始")
11
func()
12
print("装饰器B:结束")
13
14
15
# 装饰器链:从下到上执行(先B后A)
16
@
17
@
18
def ():
19
print("目标函数执行")
20
21
()
22
# 输出顺序:
23
# 装饰器A:开始
24
# 装饰器B:开始
25
# 目标函数执行
26
# 装饰器B:结束
27
# 装饰器A:结束
3. 总结
闭包借助保存自由变量达成“函数带状态”, 这是装饰器的根基所在;装饰器是基于闭包的, 它提供了函数增强的一种优雅方案。掌握这两者能够使你编写出更为简洁、极具模块化特点的代码, 无论是从数据封装方面, 还是到日志、缓存、权限控制等领域, 闭包与装饰器都是无处不在的。要记住: 闭包是“状态容器”, 装饰器是“函数插件”, 它们联袂构成函数式编程的核心魅力句号。
4. 学习好书推荐
能否曾经有过那种, 开始接触时觉得不难入门, 然而却一直处于在“能够看明白”以及“能够书写得好”这两者之间来回犹豫不决的情况呢? 到了现在此时此刻, 应当借助两本堪称经典的作品, 从而彻彻底底地将你的关键脉络給打通。
一个不算枯燥语法手册的, 能通过一个个真实项目, 让你从简单数据可视化开始一直到开发小游戏, 在动手乐趣里自然掌握核心概念的心路指南介绍出适合新人告别看了就忘而向真正的理解拥抱前行的《编程从入门到实践》, 是你的完美起点。
编程 从入门到实践 第3版(图灵出品)
¥81.7
京东月销量100好评率99%无理由退换京东配送
购买
当你筑牢根基, 《面向对象编程指南》会引领你步入高手之境。面向对象乃是创作出优雅且可复用代码的关键所在, 这本书以深入浅出之态揭示其核心要义。你能够学会怎样构筑更为强大的程序, 为日后投身复杂项目奠定坚实根基。
面向对象编程指南(异步图书出品)
¥62.3
京东好评率99%无理由退换京东配送旗舰店
购买
这两本书构成了一个堪称完美的成长闭环, 此闭环是从毫无基础起始, 直至成为专业开发者的过程。不管你是身为殷切渴望实现转行的职场之人, 又或者是满心期望提升自身竞争力的在校学生, 这一套组合都能够使你规避步入需耽搁半年时间的偏离正轨之路。