装饰器
闭包
就是将数据封装在一个包(区域)中,使用时再去里面取。本质上,闭包是基于函数嵌套搞出来的一种特殊嵌套
- 闭包的应用场景,1:封装数据防止污染全局
装饰器
import time
import functools
def func(a, b):
print("执行函数func....")
print(f"正在计算{a} + {b} ......")
res = a + b
time.sleep(3)
print(f"函数执行结束,结果是:{a+b}")
# 当我们要计算func执行时间时,就可以通过不改变fun内部代码,通过装饰器实现
def cal_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
print(f"开始时间:{start}")
res = func(*args, **kwargs)
end = time.time()
print(f"结束时间:{start}")
print(f"总花费时间:{end-start}")
return res
return wrapper
@cal_time
def func_1(a, b, c):
print("执行函数func....")
print(f"正在计算{a} + {b} + {c}......")
time.sleep(3)
print(f"函数执行结束,结果是:{a+b+c}")
return a + b + c
if __name__ == '__main__':
v_1 = func_1(1, 2, 3)
print(v_1)
print(func_1.__name__)
开放封闭原则
- 对扩展是开放的
任何一个程序在设计之初,都不可能做到面面俱到、实现所有的功能,并且后续不作任何更改。所以,我们要支持代码扩展,添加新的功能。 - 对修改是封闭的
既然说要允许代码扩展,那为什么又要对修改封闭么?举个例子,比如要给银行的交易功能添加或测试新的功能,如果直接修改源代码,那么造成有些交易发生异常,甚至无法交易。所以,对修改(源码)是封闭的。 - 装饰器的出现完美的遵循了这个开放封闭原则。
无参装饰器
装饰器的本质:装饰器本身是任意可调用对象,被装饰的对象也可以是任何可调用的对象。
装饰器的功能:在不修改被装饰对象源代码,以及调用方式的前提下,为其添加新的功能。
装饰器一般写法
def timer(func): # 装饰器
def wrapper():
res = func() # 执行被装饰函数代码
print(res) # 其他的逻辑代码
# returen res # 将结果返回
return wrapper # 返回功能函数名
@timer # 采用 @ + 装饰器名,写在被装饰函数上面,完成装饰
def index(): # 被装饰器函数
print('index function') # 逻辑代码
index() # 执行index函数,自动触发装饰器函数的执行
第7代码的意思是拿到位于下方函数的函数名当成timer函数的参数,并执行timer函数,得到timer函数的返回值赋值给变量index (index = timer(index))
import time
def timer(func):
''' prints function time '''
def wrapper():
start = time.time()
func()
print('function %s run time %s' % (func.__name__, time.time() - start))
return wrapper
@timer # foo = timer(foo)
def foo():
time.sleep(1)
foo()
上面的代码流程是:
第1步:首先在第1行导入time模块。然后程序往下走。
第2步:在第2行定义timer函数。
第3步:接着执行到第9行,发现装饰器的语法糖。这一步你可以想象语法糖默默的帮我们做了定义foo函数,并且执行foo = timer(foo)这一步赋值操作。
第4步:然后触发timer函数执行,执行timer内部代码,程序执行第4行,定义wrapper函数。只是定义,所以程序往下走。
第5步:接着在第8行将wrapper函数名返回。
第6步:timer函数暂时执行完毕,期间做了包括将执行timer函数并为func形参传递实参foo函数名,在嵌套作用域内"记住"foo变量(foo函数名),经过这一些列操作。程序从新回到语法糖的第9行并继续往下走。
第7步:因为在第3步骤时,语法糖帮我们定义了foo函数,此时就直接执行到12行,执行foo加括号。关键点:此时的foo变量已经不是最初的foo函数那个函数名了,而是在第3步骤中拿到的foo变量,而这个foo变量实为timer函数的返回值——wrapper函数名。加括号执行的是wrapper函数。所以,此时程序执行第4行的wrapper函数,接着执行内部代码。
第8步:程序执行到第5行,通过time模块获取到当前的时间戳,程序往下走。
第9步:执行到了第6行,在局部作用域去找变量func,没找到,往上去嵌套作用域里找,这次找到了,在第3步骤中,语法糖给func传递了foo函数名,此时func加括号等价于foo函数加括号执行。
第10步:程序再次通过第9行的语法糖开始执行到第11行的foo函数的内部代码,“睡”1秒,至此,被装饰函数的内部代码执行完毕。程序往下走。
第11步:第6行的func加括号执行完毕,继续执行到了第7行的打印,通过__name__拿到了func的函数名为foo,再一次获取当前的时间戳并减去第5行时获得的时间戳,算出程序运行了多少时间,通过占位符格式化完毕,打印出结果。wrapper函数执行完毕,回到第12行。foo加括号执行完毕,继续往下走,没有代码,程序结束。
本文来自博客园,作者:覃空万里,转载请注明原文链接:https://www.cnblogs.com/qinkwl/p/18622464
浙公网安备 33010602011771号