闭包和装饰器
闭包部分参考文档:https://www.cnblogs.com/bailo/p/9096937.html
闭包函数:
定义:内层函数对外层函数非全局变量的引用,就叫做闭包函数。
闭包会一直存在内存当中,不会因为函数执行结束而被释放。
# 闭包函数
def wrapper():
name = 'alex'
def inner():
# 内层函数对外层函数非局部变量的引用
print(name)
inner()
wrapper()
判断是否为闭包函数
name = 'alex'
def wrapper():
def inner():
print(name)
inner()
print(inner.__closure__) # None 说明该函数不是闭包函数
wrapper()
上述函数改写为闭包函数如下
name = 'alex'
def wrapper(x):
x = name
def inner():
print(x)
inner()
print(inner.__closure__) # <cell at 0x0000000000130C48: str object at 0x0000000001E8A260> 有类似的打印信息说明该函数为闭包函数
wrapper(name)
应用场景:
# 功能获取网页内容
from urllib.request import urlopen
def zhua(url):
def get():
return urlopen(url).read()
return get
zhua('www.baidu.com')
总结:通过闭包的方式,在该函数外层包一层作用域(即再定义一个函数),将需要的非全局变量包在外部函数中
装饰器:
定义:用来装饰其他函数,装饰器本身可以是任意可调用对象,被装饰的对象也可以是任意可调用对象。装饰器的本质就是闭包
原则:
1、不修改被装饰对象的源代码。
2、不修改被装饰对象的调用方式。
功能:在遵循原则的前提,为被装饰对象添加上新功能。
python提出了一个语法糖 @,python解释器如果到@符号就将正下方的函数名作为一个参数传给@符号后面跟着的函数。
统计函数的执行时长
# 例如 很多方法 要统计下函数的执行时长
def add(a, b):
return a+b
add(1, 2)
方法一:
import time
def add():
print("add run")
start_time = time.time()
time.sleep(3)
end_time = time.time()
print("add end")
print("last_time:{}".format(end_time-start_time))
add()
方法二:
# 例如 很多方法 都要统计下函数的执行时长
import time
# 装饰器加载过程
# 在函数还未调用时 就已经加载执行了装饰器对应的 print("dec started")
# 所以要采用闭包函数,把其他要执行的内容 放于内部的函数,防止一开始就直接执行了
# 可以将main函数注释掉 只运行方法,发现是可以打印 “dec started“
def time_dec(func):
print("dec started")
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print("last_time:{}".format(end_time - start_time))
return wrapper
# @语法糖表示:将其修饰的函数add,传入至@后面的方法time_dec中
@time_dec
def add():
print("add run")
time.sleep(3)
print("add end")
if __name__ == '__main__':
add()
在方法二的代码中,如果执行 add(a, b),使a+b,会出现参数错误,解决方案见方法三:
用到了functools.wraps(),和参数传递的*args,**kwargs
# 例如 很多方法 都要统计下函数的执行时长
import time
import functools
def time_dec(func):
print("dec started")
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
print("last_time:{}".format(end_time - start_time))
return wrapper
# @语法糖表示:将其修饰的函数add,传入至@后面的方法time_dec中
@time_dec
def add(a, b):
print("add run")
time.sleep(3)
print("a+b:{}".format(a+b))
print("add end")
if __name__ == '__main__':
add(1, 2)