一、装饰器
装饰器本质上也是函数,其功能是为被装饰的函数添加附加功能。装饰器的使用原则:(1)不能修改被装饰函数的源代码;(2)不能修改被装饰函数的调用方式,总之,装饰器对被装饰函数来说是透明的。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。
1、预备知识:
(1)高阶函数:把一个函数名当作实参传递给另一个函数,返回值中包含函数名
(2)嵌套函数:在一个函数体内声明一个函数,不是只调用一个函数
(3)装饰器=高阶函数+嵌套函数
2. 简单装饰器
假如要统计一个函数的执行时间,我们可以写一个统计时间的函数,然后将被统计的函数作为参数传递
1 import time 2 def bar(): 3 time.sleep(3) 4 print(‘in the bar‘) 5 def test1(func): 6 start_time=time.time() 7 func() 8 stop_time = time.time() 9 print(‘the func run time is %s‘ % (stop_time - start_time)) 10 test1(bar)
运行结果:
in the bar the func run time is 3.000171661376953
但是这样的话,我们每次都要将一个函数作为参数传递给test1函数。改变了函数调用方式,之前执行业务逻辑时,执行运行bar(),但是现在不得不改成test1(bar)。此时就要用到装饰器。我们就来想想办法不修改调用的代码;如果不修改调用代码,也就意味着调用bar()需要产生调用test1(bar)的效果。我们可以想到将test1赋值给bar,但是test1似乎带有一个参数……想办法把参数统一吧!如果test1(bar)不是直接产生调用效果,而是返回一个与foo参数列表一致的函数的话……就很好办了,将test1(bar)的返回值赋值给bar,然后,调用bar()的代码完全不用修改!
1 import time 2 def timmer(func): 3 def deco(): 4 start_time=time.time() 5 func() 6 stop_time=time.time() 7 print(‘the func run time is %s‘%(stop_time-start_time)) 8 return deco 9 10 def bar(): 11 time.sleep(3) 12 print(‘in the bar‘) 13 bar=timmer(bar) 14 bar()
运行结果:
in the bar the func run time is 3.000171661376953
函数timmer就是装饰器,它把执行真正业务方法的func包裹在函数里面,看起来像bar被timmer装饰了。如果我们要定义函数时使用装饰器,避免使用赋值语句bar=timmer(bar),要用到装饰器的语法糖@
1 import time 2 def timmer(func): 3 def deco(): 4 start_time=time.time() 5 func() 6 stop_time=time.time() 7 print(‘the func run time is %s‘%(stop_time-start_time)) 8 return deco 9 @timmer 10 def bar(): 11 time.sleep(3) 12 print(‘in the bar‘) 13 bar()
运行结果:
in the bar the func run time is 3.000171661376953
这样,我们就提高了程序的可重复利用性,当其他函数需要调用装饰器时,可以直接调用。装饰器在Python使用如此方便都要归因于Python的函数能像普通的对象一样能作为参数传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内。
3.带有参数的装饰器
如果要装饰的函数带有参数时
1 import time 2 def timmer(func): 3 def deco(*arg,**kwarg): 4 start_time=time.time() 5 func(*arg,**kwarg) 6 stop_time=time.time() 7 print(‘the func run time is %s‘%(stop_time-start_time)) 8 return deco 9 @timmer 10 def test1(): 11 time.sleep(1) 12 print(‘in the test1‘) 13 @timmer 14 def test2(name,age) : 15 time.sleep(2) 16 print(‘in the test2:‘,name,age) 17 test1() 18 test2(‘Alex‘,18)
运行结果:
in the test1 the func run time is 1.0000572204589844 in the test2: Alex 18 the func run time is 2.0001144409179688
二、生成器
列表在使用前数据就已经生成了,但是我们往往只使用其中一部分数据,大部分数据用不到浪费空间。生成器generator只有在调用时才生成相应的数据。
1. 列表生成式:如果要生成列表[1x1, 2x2, 3x3, ..., 10x10]怎么做?除了循环还可以用一行语句代替循环生成
1 list=[x*x for x in range(1,11)] 2 print(list)
运行结果:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
这种写法就是Python的列表生成式,写列表生成式时,把要生成的元素 x * x 放到前面,后面跟 for 循环,就可以把list创建出来。
2. 生成器:要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个generator
1 g=(x*x for x in range(1,11)) 2 print(g)
运行结果:
<generator object <genexpr> at 0x005B37E0>
创建list
和generator
的区别仅在于最外层的[]
和()。list的元素我们可以一个个打印出,如果要打印generator中的元素需要借助next方法
1 g=(x*x for x in range(1,11)) 2 print(next(g)) 3 print(next(g)) 4 print(next(g))
运行结果:
1 4 9
但是generator保存的是算法,每次调用next(g)
,就计算出g
的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration
的错误。可以通过for
循环来迭代它,并且不需要关心StopIteration
的错误。
1 g=(x*x for x in range(1,11)) 2 for i in g: 3 print(i)
运行结果:
1 1 2 4 3 9 4 16 5 25 6 36 7 49 8 64 9 81 10 100
3. 用函数生成generator:generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for
循环无法实现的时候,还可以用函数来实现。例如,斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:
1 def fib(max): 2 n,a,b=0,0,1 3 while n<max: 4 print(b) 5 a,b=b,a+b 6 n=n+1 7 return ‘done‘ 8 f=fib(6)
运行结果:
1 1 2 1 3 2 4 3 5 5 6 8
上面的函数和generator仅一步之遥。要把fib
函数变成generator,只需要把print(b)
改为yield b
就可以了: