运行结果如下:
制作一杯波霸奶茶 耗时 1.004335880279541
这个时候问题来了,除了波霸奶茶还有四季奶绿这款奶茶:
def 四季奶绿(): time.sleep(2) print("制作一杯四季奶绿")
而我也想知道四季奶绿的制作时间,需要把统计时间那部分代码粘贴复制一下,有点繁琐,如果还有一款格雷三兄弟,也是要复制下,好繁琐啊。有没有什么方法能简化这个过程呢?答案肯定是有的:「Python支持函数作为参数
」,我们可以把计时部分剥离成一个单独的函数,然后把需要查看制作时间的奶茶函数传入即可,所以有了下面这样的代码:
def 波霸奶茶(): time.sleep(1) print("制作一杯波霸奶茶") def 四季奶绿(): time.sleep(2) print("制作一杯四季奶绿") def 计时(func): start = time.time() func() end = time.time() print("耗时", end - start) if __name__ == '__main__': 计时(波霸奶茶) 计时(四季奶绿)
运行结果如下:
制作一杯波霸奶茶 耗时 1.0002198219299316 制作一杯四季奶绿 耗时 2.0016701221466064
可以,这个时候问题又来了,上面的代码意味着:所有调用到制作奶茶方法的地方都要改成计时(奶茶),如果奶茶有N多种的话,每个调用制作奶茶的函数都要改成这样的形式,有没有更简单的方法啊?答案肯定是有的,Python支持返回一个函数。我们要做的是通过一个内嵌的包装函数,为传入的函数附加功能,然后返回这个函数,具体的代码如下:
def 计时(func): def decorator(): start = time.time() func() end = time.time() print("耗时", end - start) return decorator if __name__ == '__main__': 波霸奶茶 = 计时(波霸奶茶) 波霸奶茶() 四季奶绿 = 计时(四季奶绿) 四季奶绿()
运行结果如下:
制作一杯波霸奶茶 耗时 1.0033059120178223 制作一杯四季奶绿 耗时 2.0044848918914795
可以,没毛病,而且只有在我们需要查询某种奶茶的制作时间时才需要这样写,平常的函数还是正常使用。嗯,你以为到这里就完了,对于装饰器函数,Python还为我们提供了一枚语法糖,语法糖可以理解成一种便捷的写法吧,在Python中通过一个@就可以简化我们上面的代码,具体代码如下:
def 计时(func): def decorator(): start = time.time() func() end = time.time() print("耗时", end - start) return decorator @计时 def 波霸奶茶(): time.sleep(1) print("制作一杯波霸奶茶") @计时 def 四季奶绿(): time.sleep(2) print("制作一杯四季奶绿") if __name__ == '__main__': 波霸奶茶() 四季奶绿()
一样的运行结果,更加精简的代码,相信读者都体验到了语法糖的便利,但是要注意一点:大部分的语法糖只是减少我们写繁琐代码的时间,让我们把更多的时间花在逻辑流程上,代码写少了不一定就会带来性能上的提升,底层的代码可能还是那些东西!
② 带参数的装饰器
上面演示的是无参数的装饰器的用法,有时我们可能需要传入一些参数,需要在原闭包的基础上再加一层闭包
,具体流程与代码示例如下:
import time def 计时(配料="珍珠"): def decorator(func): def inner(): print("加入配料:%s" % 配料 ) start = time.time() func() end = time.time() print("耗时", end - start) return func return inner return decorator @计时(配料='波霸') def 波霸奶茶(): time.sleep(1) print("制作一杯波霸奶茶") @计时(配料='椰果') def 四季奶绿(): time.sleep(2) print("制作一杯四季奶绿") if __name__ == '__main__': 波霸奶茶() print() 四季奶绿()
运行结果如下:
加入配料:波霸 制作一杯波霸奶茶 耗时 1.0022242069244385 加入配料:椰果 制作一杯四季奶绿 耗时 2.0034420490264893