装饰器
在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