python 迭代器与生成器

原创
2017/01/28 14:47
阅读数 53

python 迭代器与生成器


说到python迭代器,首先要明确两个概念:IterableIterator,这两个概念还有Generator都是定义在collections模块里的。

Iterable意为“可迭代的(对象)”,包括如下两种:

1、实现了__getitem__(self,index)方法的对象

这类对象用for关键字迭代时,索引从0开始递增,每次+1, 直到__getitem__方法抛出IndexError或者StopIteration错误则停止迭代。

2、实现了__iter__(self)方法的对象

约定:返回的对象必须包含__next__(self)方法, 用for关键字迭代时,__next__方法抛出StopIteration错误则停止迭代。

一般来说,list、tuple都是实现了__getitem__(self,index)方法因而可迭代,而dict则是实现了__iter__(self)而可迭代。可迭代对象都可以被for关键字迭代,也可以用内建函数iter转化为迭代器对象。

Iterator即迭代器对象。迭代器和可迭代对象不同。迭代器的要求是:

1、实现了__next__(self)方法 2、实现了__iter__(self)方法

约定:__iter__(self)方法应返回自身

以上的约定并非强制的,即使不遵守也可以通过isinstance的检查,但为避免错误,建议编写代码时遵守约定。

Generator即生成器,生成器都实现了迭代器协议,因此所有的生成器都是迭代器。生成器除了迭代器的两个方法之外,还要包含如下方法:

  1. send(self,value)方法,用于向生成器发送值;
  1. throw(self,error)方法,用于向生成器抛入错误;
  2. close(self)方法,用于关闭生成器;

如果一个迭代器具有上述三个方法,就被识别为生成器。

生成器的约定:

  1. send方法用于向生成器发送一个值,一个生成器接受的第一个值必须是None,即一个生成器第一次调用必须是__next__方法,该方法等价于send(None)
  1. close方法的效果是在生成器中产生一个GeneratorExit异常,异常的作用是触发生成器中可能的try...except...finally的finally块,或者with block的__exit__方法。
  2. throw方法的作用是向生成器中抛入异常。可以抛入GeneratorExit异常。也可以抛入其他异常。生成器可以利用不同异常进行不同的善后处理,但不应捕获GeneratorExit异常。

生成器当然可以使用class实现Generator协议的各个方法和约定来实现。但通常,我们使用如下两个方法来创建生成器:

  1. 生成器解析式,类似列表解析,但将中括号替换成小括号。
  1. 使用yield来创建生成器。如果一个函数内部使用了yield,该函数执行的结果就是一个生成器。

关于后者,有如下知识点:

  1. 关键字yield的语法是yield x或者y=yield x,其操作是先将x输出,然后调用send方法或者__next__方法后继续从yield后执行。
  1. 如果调用send方法,yield会接收传递的值并赋给y;如果调用__next__方法,yield接收到的值为None。
  2. 调用throw方法可以将异常抛入生成器内,异常从yield处进入生成器,随后就进入异常处理途径,被捕获处理或者继续向外抛出。GeneratorExit异常不应被处理。
  3. 调用close方法生成的GeneratorExit异常也是从yield处出现的。close方法会在最后捕获该异常,不会抛出该异常到外部。
  4. 生成器代码块里的return语句的返回值将作为StopIteration异常类的构造函数的参数来生成该异常,捕获该异常,其value字段即为返回值。

附注:协程旧语法yield from的知识点

  1. 关键字yield from的语法为x=yield from B,其中B为一个生成器。生成的协程函数(设其为A)同样满足生成器协议。
  1. 所有通过send方法发送到A的数据,如果值为None则调用B的__next__方法;否则将调用B的send方法将值直接传递给B。
  2. 如果调用A的close方法,则B的close方法也会被调用。
  3. 如果通过A的throw方法抛入GeneratorExit异常,会调用B的close方法,然后再从A的yield from处抛出该异常。
  4. 如果通过A的throw方法抛入非GeneratorExit的异常,会调用B的throw方法将异常直接抛入B中。
  5. 如果B抛出StopIteration异常,该异常的value字段值将赋给x,而A将从yield from后继续执行。
  6. 如果B抛出非StopIteration的异常,则该异常会从yield from处抛入A中。

yield from目前基本不被用作协程之间的交流了,现在都用async...await语法来实现协程。但是yield from仍然有用,也就是可以用作生成器的修饰,即增加生成器的上下文处理。

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部