Python3基础-迭代器&生成器

本节内容

  • 列表生成
  • 生成器
  • 迭代协议
  • 可迭代对象&迭代器

 列表生成 

>>>a=[x*x for x in range(10)]
>>>a
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

  

生成器(generator)概念

在python中为节约内存空间,对延迟操作提供了支持,所谓的延迟操作(惰性)即需要的时候才产生结果,而不是和List一样马上产生全部结果。

python两种方式提供生成器:

  • 生成函数:用常规函数定义,使用 yield 内置方法返回结果而不是return,yield 一次只返回一个结果,再每个结果结束后,处于挂载状态,以便下次重离开的地方继续执行
  • 生成表达式:类似列表生成,把 [] 换成 () 使用,返回的是一个生成器对象

 生成函数 

  • ----在python中yield就是一个生成器,通过方法yield返回结果
  • ----那么该函数就不再是普通函数,而是生成器函数。
  • ----再没有要求生成器产生结果时,函数内不会被访问输出
def gen(n):
    for i in range(n):
        yield  i*2 #通过方法yield返回结果
r=gen(5)
print(r)
print(r.__next__())
print(next(r))

结果:
<generator object gen at 0x1021e2db0>
runing
0
runing
2

#普通的列表生成器
def gen_1(n):
    res=[]
    for i in range(n):
        res.append(i*2)
    return res

r_1=gen_1(5)
print(r_1)

结果:
[0, 2, 4, 6, 8]

  

 生成表达式 

a=(x*x for x in range(10))
print(a)

结果:
<generator object <genexpr> at 0x1020e2f10>

Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:

>>>sum(x ** 2 for x in range(4)) 
14

总结:

  • 自动实现迭代器协议:在python中会自动实现迭代器协议,可以调用next(obj) ___next__() 方法去取值,如何到最后没有值返回时,抛出 StopIteration 异常
  • 状态挂起:生成器使用yield 返回值,yield 方法保存生成器函数的状态,每次要求产生值时,回到 yield 挂起出继续下一个执行(在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行)
  • 第一次执行next(generator)时,会执行完yield语句后程序进行挂起,所有的参数和状态会进行保存。再一次执行next(generator)时,会从挂起的状态开始往后执行。在遇到程序的结尾或者遇到StopIteration时,循环结束。
  • 可以通过generator.send(arg)来传入参数,这是协程模型。
  • 可以通过generator.throw(exception)来传入一个异常。throw语句会消耗掉一个yield。可以通过generator.close()来手动关闭生成器。
  • next()等价于send(None)

支持的方法:

close()

手动关闭生成器函数,后面的调用会直接返回StopIteration异常。

def g4():
    yield 1
    yield 2
    yield 3
g=g4()
print(next(g))
g.close()
next(g)    #关闭后,yield 2和yield 3语句将不再起作用

 send()

生成器函数最大的特点是可以接受外部传入的一个变量,并根据变量内容计算结果后返回。
这是生成器函数最难理解的地方,也是最重要的地方,实现后面我会讲到的协程就全靠它了。

  • send<<===>>yield  send传入值给yield触发执行yield并挂起等待下一次执行
  • 通过 g.send(None) 或者 next(g) 可以启动生成器函数

 

def gen():
    value = 0
    while True:
        receive = yield value
        if receive == 'e':
            break
        value = 'got: %s' % receive

    print('ok ')
g = gen()
print(g.send(None))
print(g.send('aaa'))
print(g.send(3))
print(g.send('e'))#已经退出循环没有接收到yield返回值

结果:
0
got: aaa
got: 3
ok 
StopIteration异常

 throw()

用来向生成器函数送入一个异常,可以结束系统定义的异常,或者自定义的异常。
throw()后直接跑出异常并结束程序,或者消耗掉一个yield,或者在没有下一个yield的时候直接进行到程序的结尾。

def gen():
    value = 0
    while True:
        try:
            receive = yield value
            if receive == 'e':
                break
            value = 'got: %s' % receive
        except ValueError:
            print('valueError!!!')
        except TypeError:#异常判断
            print('TypeError!!!')

    print('ok ')
g = gen()
print(g.send(None))
print(g.send('aaa'))
g.throw(TypeError)#传入一个异常
print(g.send(3))

 

 迭代器Iterable 

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

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

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

可迭代对象:

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

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

from collections import Iterable  #导入模块
print(isinstance([],Iterable))
print(isinstance((),Iterable))
print(isinstance({},Iterable))
print(isinstance('ABC',Iterable))
print(isinstance(100,Iterable))


结果:
True
True
True
True
False 

 迭代器 

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

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

>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

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

 Iterable变成Iterator 

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

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

  

 

 

 

posted @ 2017-08-13 22:01  Ronny_bin  阅读(101)  评论(0)    收藏  举报