Python进阶之闭包和装饰器


  • 💂 个人主页:风间琉璃
  • 🤟 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主
  • 💬 如果文章对你有帮助欢迎关注点赞收藏(一键三连)订阅专栏

前言

一般来说闭包这个概念在很多语言中都有涉及,本文主要谈谈python中的闭包定义和相关用法以及装饰器。Python中使用闭包主要是在进行函数式开发时使用,而装饰器(语法糖)在闭包的基础上为函数添加额外的功能,并且在不改变原函数代码的情况下


一、闭包

在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。 —— 维基百科

闭包:在一个内部函数中,对外部作用域的变量进行引用,(并且一般外部函数的返回值为内部函数),那么内部函数就被认为是闭包。当某个函数被当成对象返回时,夹带了外部变量,就形成了一个闭包, 可以这样理解,闭包就是能够读取其他函数内部变量的函数
在这里插入图片描述
在函数f1中定义了一个f2函数,f2访问了外部函数f1的变量,并且f1返回了f2函数,在python中是可以返回一个函数的。在上面中a其实就是一个函数,a就是f2。
在这里插入图片描述如果调用函数a,得到的结果是传入参数的整数值加,所以结果为2。一个函数中若要用到另一个函数的参数,则可以通过闭包的形式来实行,闭包,即封闭,包含,内层函数中定义的变量是无法在外层函数进行使用的,即在f2中可以调用x,但是不能在f1函数中调用y。此外可以直接调用函数f1(),但是不能调用函数f2()。

小结:
1.闭包定义是在函数内再嵌套函数。
2.闭包是可以访问另一个函数局部作用域中变量的函数
3.闭包可以读取另外一个函数内部的变量
4.闭包可以让参数变量不会被垃圾回收机制回收始终保持在内存中(而普通的函数调用结束后 会被Python解释器自动释放局部变量)

二、装饰器

装饰器是Python中一种非常有用的语法,它可以在不改变原函数代码的情况下为函数添加额外的功能。装饰器可以用于日志记录性能测试事务处理缓存等方面。通过装饰器,我们可以将这些功能与原函数分离开来,使得代码更加简洁、易于维护和复用。

对于装饰器的定义,基于函数闭包的形式来实现即可以将某一个函数作为参数传递给另一个函数,在这另一个函数中去为函数添加功能. 与普通函数相比,其参数是一个函数,并且返回值是一个函数

举个例子,我们可以使用装饰器来记录函数的运行时间,代码如下所示(注:下面代码中的@timer就是一个装饰器函数):

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print('函数 %s 运行时间为 %s 秒' % (func.__name__, end_time - start_time))
        return result
    return wrapper

@timer
def my_func():
    time.sleep(2)

my_func()

上述代码中,我们定义了一个装饰器函数timer它接受一个函数作为参数,并返回一个新的函数wrapper。wrapper函数中,我们记录了函数的开始时间和结束时间,并计算出函数的运行时间。最后,我们将运行时间打印出来,并返回原函数的结果。在使用装饰器时,我们只需要在原函数的定义前加上@timer即可。

下面再来看看装饰器的原理
在这里插入图片描述
代码原理及执行循序如下:
(1)当运行该程序代码时,执行到@decorator时,就会去调用decorator函数,发现该函数是一个装饰函数,向下继续执行。

(2)这里被装饰的函数func1会作为参数传递给decorator函数,即decorator中的参数func=func1。

(3)然后开始执行内部函数wrapper,先是打印语句,然后是func函数,因为刚刚把func1传递给func,所以这里执行的是func1(),但该处并不是把func1函数下的代码运行出来,而是decorator函数返回值是内部函数wrapper,该处返回的是加了装饰器的func1函数,原始函数func1函数的地址已经指向了decorator.wrapper的函数地址。

(4)程序执行@decorator后,再执行func1(),func1会指向decorator.wrapper的函数地址,所以代码输出结果为:执行decorator.wrapper函数,先打印“我喜欢你”,然后执行func()也就是被当成参数传进来的原始函数func1,打印“我也喜欢你”。经过上述步骤,func1就被装饰了。

对于两个装饰器来说,执行顺序如下:
在这里插入图片描述
程序运行后,依次运行@decorator2,@decorator1:
(1)程序运行后,首先运行到@decorator2时,发现是一个装饰器函数,需要对下面的函数进行装饰。因此向下执行寻找decorator2中的参数func,但是下一行是@decorator1,不是一个函数名,是一个装饰器函数。此时,@decorator2暂时执行,执行接下来的装饰器@decorator1,然后向下执行找到函数名func。

(2)func作为参数传给装饰器@decorator1,@decorator1开始执行,打印"装饰器1开始对原始函数进行装饰", 然后@decorator1内部函数开始对func进行装饰,装饰之后,decorator1返回值为内函数wrapper。此时,原始函数func已经指向了decorator1的内函数wrapper的地址,即func = deco1.wrapper。

(3)然后返回继续执行@decorator2,把新的func(@decoratoe1装饰之后的func)作为参数传给decorator2函数。

(4)执行decorator2函数,打印"装饰器2开始对函数进行装饰",然后@decorator2内部函数开始对新的func进行装饰,装饰之后,decorator2返回值为内函数wrapper,此时,新函数func又指向了decorator2的内函数wrapper的地址,即func = deco2.wrapper。经过上述步骤,@decorator2和@decorator1都已经运行,并且func函数已经被两个函数进行装饰了。

(5)然后继续执行 ret = func(), 即运行原始函数func函数,由于func经过装饰后,指向的是decorator2.wrapper,从而运行decorator2.wrapper函数,因此打印出“装饰器2内函数开始执行”,同时继续执行代码:return " [002] " + func() + " [002] “,即返回一个” [002] " + func() + " [002] “,此时返回值中的func()最外层已经被装饰[002]。语句中的func()执行的时寻找到的是func的上一级地址decorator1.wrapper(decorator2.wrapper地址已经使用了)。返回值中的func()即decorator1.wrapper运行后的返回值:” (001) " + func() + " (001) “。 因此decorator2.wrapper函数返回的实际是:” [002] " + " (001) " + func() + " (001) " + " [002] "。

(6)decorator1.wrapper运行时,首先打印“装饰器1内函数开始执行”,同时继续执行代码:return " (001) " + func() + " (001) “。这时语句中的func()才是指向的原始函数func的地址,执行原始函数func,打印出“func函数执行”。原始函数func的返回值是:“Hello Decorator”。因此最终func函数运行后的返回值是:” [002] " + " (001) " + “Hello Decorator” + " (001) " + " [002] "。最后执行print(ret)即打印出经过装饰之后func函数的返回值

总之,装饰器装饰的时,从最靠近原始函数开始进行装饰,逐级向外进行(外函数),所有装饰函数装饰结束后,再执行原始函数,此时原始函数指向的是最后一个装饰器内函数的地址,内函数又从外面的装饰器开始执行,逐级向里面执行(内函数),所有内函数开始执行完了,最后开始执行原始函数下的代码。先里后外,再外后里,最后原始。

多个装饰器装饰器函数运行顺序:
(1)最靠近原始函数的装饰器首先运行,然后运行靠近该装饰器的第二个装饰器
依次启动完所有的装饰器,运行完所有装饰器中的外函数,

(2)然后启动主程序,内函数从外层向里层运行。注意,如果装饰器中内函数前面没有外函数,还是先运行所有的装饰器,然后运行原始函数,

装饰器总结
(1)对原函数功能的补充:日志记录
(2)对原函数功能的调整:利用原函数运行结果,再次运算产生新的结果
(3)对原函数功能的重写:仅使用原函数名

结束语

感谢阅读吾之文章,今已至此次旅程之终站 🛬。

吾望斯文献能供尔以宝贵之信息与知识也 🎉。

学习者之途,若藏于天际之星辰🍥,吾等皆当努力熠熠生辉,持续前行。

然而,如若斯文献有益于尔,何不以三连为礼?点赞、留言、收藏 - 此等皆以证尔对作者之支持与鼓励也 💞。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Super.Bear

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值