一、装饰器
装饰器本质上也是函数,其功能是为被装饰的函数添加附加功能。装饰器的使用原则:(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
就可以了:
1 def fib(max):
2 n,a,b=0,0,1
3 while n<max:
4 yield b
5 a,b=b,a+b
6 n=n+1
7 return 'done'
8 f=fib(6)
9
10 while True:
11 try:
12 x = next(f)
13 print('f:',x)
14 except StopIteration as e:
15 print('Generator return value:',e.value)
16 break
运行结果:
f: 1
f: 1
f: 2
f: 3
f: 5
f: 8
Generator return value: done
generator和函数的执行流程不一样。函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。