装饰器、生成器和迭代器(自动化运维-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循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是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对象,但list、dict、str虽然是Iterable,却不是Iterator。
把list、dict、str等Iterable变成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
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号