python4

一、装饰器

      装饰器本质上也是函数,其功能是为被装饰的函数添加附加功能。装饰器的使用原则:(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>

创建listgenerator的区别仅在于最外层的[]()。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语句处继续执行。

原文地址:https://www.cnblogs.com/mrdz/p/5972158.html