Python——装饰器和闭包

文章介绍了Python中的装饰器概念,用于在不改变原函数代码的情况下增加额外功能,如计算程序执行时间。装饰器通过定义包装函数并将其赋值给原函数实现,也可以使用语法糖`@decorator`简化这一过程。文章还探讨了闭包的概念,即内部函数引用外部函数的非局部变量,并通过示例展示了如何处理UnboundLocalError以及如何通过外部变量记住内部函数。
摘要由CSDN通过智能技术生成

装饰器

在Python的代码书写中,我们常常看到类似于不改变原来函数,给函数添加新的功能类似的问题,而这些问题会让程序员非常的头疼,于是Python便提出了装饰器语法

先写一个求程序执行时间的函数

import time

def call():
    t1 = time.time()
    time.sleep(3)
    print(f"程序执行的时间为{time.time()-t1}")

if name == '__main__':
    call()

如果我们此时还有一个函数,同样需要执行求程序执行时间的功能,作为cv工程师而言,是不需要重复造轮子的,所以我们使用装饰器试试:

import time

def call(t1):
    time.sleep(3)
    print(f"程序执行的时间为{time.time()-t1}")


def wrap(tmp):
    def myfunc():
        t1 = time.time()
        tmp(t1)
    return myfunc
    

if __name__ == '__main__':
    call = wrap(call)
    call()

这里的wrap就是一个装饰器
有的小伙伴可能对于 call = wrap(call)这句话有点难理解,我们可以把这行代码分开进行理解,先理解=右边的内容
warp(call)的意思是调用wrap函数,并且把call函数当做参数传入该函数,由于wrap函数直接返回myfunc函数,所以这句话可以直接理解为call = myfunc,此时再call()调用call函数就相当于在调用myfunc函数
并且此时的myfunc函数已经传承了call函数的计算程序执行时间的功能

但是作为“高贵”的Python程序员,Python就一切从简的规则,对如此复杂的装饰器必然不可能视而不见,于是出现了装饰器的语法糖

语法糖

这里可能有小伙伴就不是很理解了,什么叫做语法糖,是可以吃的那种吗?

语法糖是对语法整体并不存在影响,但是可以简化程序员书写的一种语法形式,例如我们前面讲到的列表的切片等等操作便是一种语法糖

装饰器的语法糖

Python在装饰器的语法糖上,一般是用**@+装饰器名称**来完成的

import time

def wrap(tmp):
    def myfunc():
        t1 = time.time()
        tmp(t1)
        print("hello csdn")
    return myfunc

@wrap
def call(t1):
    time.sleep(3)
    print(f"程序执行的时间为{time.time()-t1}")

if __name__ == '__main__':
    call()

利用了@的语法糖后,我们可以发现在调用的时候没有必要再进行复杂的赋值操作,而是简单的调用我们需要的函数call,这样call就因为装饰器wrap多了一个print功能

注意:此时装饰器的默认修饰对象是装饰器函数传入的参数函数,例如此时装饰器默认的函数便是tmp函数也就是call函数

闭包

在Python的官方解释中,闭包语法是这样解释的:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用

这样看这个定义可能有点苦涩难懂,我们可以先结合代码来看看,首先我们先来看一串代码:

def init():
    x = 5
    def add():
        x += 5
        return x
    return add

if __name__ == '__main__':
    c = init()
    print (c())

大家可以先观察一下这段代码存不存在问题,如果你认为这串代码并不存在问题,那么文章的后半部分就得好好的仔细理解了

这里存在报错

Traceback (most recent call last):
  File "C:/Users/lenovo/AppData/Local/Programs/Python/Python310/test8.py", line 12, in <module>
    print (c())
  File "C:/Users/lenovo/AppData/Local/Programs/Python/Python310/test8.py", line 6, in add
    x += 5
UnboundLocalError: local variable 'x' referenced before assignment

这里的报错大概的意思是,在add函数中使用了外部函数的局部变量x,所以会导致报错,那么如何修改呢,我们只需要在add函数中告诉计算机x并不是一个本地局部变量,也就是nonlocal语句

def init():
    x = 5
    def add():
    	nonlocal x
        x += 5
        return x
    return add

if __name__ == '__main__':
    c = init()
    print (c())

这样这个程序就可以成功的运行了

除了使用nonlocal语句之外,我们在普通的函数调用中,我们还可以用外部的变量来保存内部函数的方法来实现一种闭包

def add1(x):
    def add2(y):
        return x+y
    return add2

c = add1(2)
print(c(9))

这里要注意理解的是在c = add1(2)后,c到底是一个什么东西,我们可以用我们的IDLE来进行调试:
在这里插入图片描述
显然在此时的c是一个函数的类型,那我们就不由得发出疑问:是add1函数还是add2函数呢?
没关系我们可以继续用IDLE进行调试:

显然在这个时候c是add2函数了,所以我们成功的在函数外部利用c变量记住了内部函数c2

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值