装饰器、生成器和迭代器(自动化运维-6)

一、生成器

列表生成式

i = [i*i for i in range(10) if i%2 == 0]
print(i)
#执行结果
[0, 4, 16, 36, 64]

生成器

如果列表元素可以按照某种算法推算出来,在循环的过程中不断推算出后续的元素,这种一边循环一边计算的机制,称为生成器:generator。

创建生成器:

方法 1

只要把一个列表生成式的[]改成(),就创建了一个generator:

i = (i*i for i in range(10) if i%2 == 0)
print(i)
#执行结果
<generator object <genexpr> at 0x000001F7F9726A98>

通过next()函数获得generator的下一个返回值。

通过for循环来迭代它,因为generator也是可迭代对象。

i = (i*i for i in range(10) if i%2 == 0)
print(next(i))
print(next(i))
print("分割线".center(50,"=")) # 注意是接着 next 继续循环的
for x in i:
    print(x)
#执行结果
0
4
=======================分割线========================
16
36
64

方法 2

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现

比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

函数转换为 generator 只需要把print(b)改为yield b就可以了:

def fib(max):
    n,a,b = 0,0,1
    while n < max:
        #print(b)
        yield  b
        a,b = b,a+b
        n += 1
    return 'done'
f = fib(10)
print(f)
print(next(f))
print(next(f))
print(next(f))
#执行结果
<generator object fib at 0x000001F48E566A98>
1
1
2

二、迭代器

我们已经知道,可以直接作用于for循环的数据类型有以下几种:

一类是集合数据类型,如listtupledictsetstr等;

一类是generator,包括生成器和带yield的generator function。

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable

可以使用isinstance()判断一个对象是否是Iterable对象:

from collections import Iterable
print(isinstance("bushaoxun",Iterable))
print(isinstance(123,Iterable))
#执行结果
True
False

而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

可以使用isinstance()判断一个对象是否是Iterator对象:

from collections import Iterable,Iterator
print(isinstance("bushaoxun",Iterator))
print(isinstance((i for i in range(10)),Iterator))
#执行结果
False
True

生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator

listdictstrIterable变成Iterator可以使用iter()函数:

from collections import Iterable,Iterator
print(isinstance(iter("bushaoxun"),Iterator))
print(isinstance(iter({}),Iterator))
#执行结果
True
True

Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

三、装饰器

装饰器,本质上是函数,用来装饰其他的函数,为其他函数添加新的功能。

特点:

  • 不改变被装饰函数的源代码
  • 不改变被装饰函数的调用方式

函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

函数对象有一个__name__属性,可以拿到函数的名字:

def xun():
    print("hello")
f = xun # 函数名赋值给变量,记住不要加括号,加括号是执行函数,把函数的返回值赋值给变量
f()
print(xun.__name__)
print(f.__name__)
# 执行结果
hello
xun
xun

装饰器 =  高阶函数+嵌套函数

本质上,decorator就是一个返回函数的高阶函数。

装饰器 1:

装饰器本身不需要传参数

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        x = func()  #执行函数
        return x    #返回原函数的返回值
    return wrapper
@log  #把@log放到xun()函数的定义处,相当于执行了语句: xun=log(xun)
def xun():
    print("hello")
print(xun)
print(xun())
#执行结果
<function log.<locals>.wrapper at 0x000002EADBFED2F0>
call xun():
hello
None

 装饰器 2:

装饰器本身需要传参数

如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。

def name(name):
    def decorator(func):
       def wrapper(*args, **kw):
           print('call %s():' % func.__name__)
           print("my name is %s" % name) # 传入装饰器参数
           x = func()  #执行函数
           return x    #返回原函数的返回值
       return wrapper
    return decorator
@name("bushaoxun") # xun=name("bushaoxun")(xun)
def xun():
    print("hello")
xun()
print(xun.__name__)
#执行结果
call xun():
my name is bushaoxun
hello
wrapper

装饰器 __name__ 属性

以上两种decorator的定义都没有问题,但还差最后一步。因为函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的'now'变成了'wrapper': 

所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

但是我们不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就能做这个,一个完整的decorator的写法如下:

import functools
def name(name):
    def decorator(func):
       @functools.wraps(func)
       def wrapper(*args, **kw):
           print('call %s():' % func.__name__)
           print("my name is %s" % name) # 传入装饰器参数
           x = func()  #执行函数
           return x    #返回原函数的返回值
       return wrapper
    return decorator
@name("bushaoxun") # xun=name("bushaoxun")(xun)
def xun():
    print("hello")
xun()
print(xun.__name__)
#执行结果
call xun():
my name is bushaoxun
hello
xun

 

posted @ 2018-03-29 17:41  步绍训  阅读(121)  评论(0)    收藏  举报