python 可迭代对象与迭代器

生成器函数的工作原理
只要 Python 函数的定义体中有 yield 关键字, 该函数就是生成器函数。 调用生成器函数时, 会返回一个生成器对象。 也就是说, 生成器函数是生成器工厂。

调用生成器函数返回生成器; 生成器产出或生成值.

构建可迭代的对象和迭代器时经常会出现错误, 原因是混淆了二者。

要知道,可迭代的对象必须能从同一个可迭代的实例中获取多个独立的迭代器, 而且各个迭代器要能维护自身的内部状态,可迭代的对象有个 __iter__ 方法, 每次都实例化一个新的迭代器,而迭代器要实现 __next__ 方法, 返回单个元素, 此外还要实现__iter__ 方法, 返回迭代器本身。

因此, 迭代器可以迭代, 但是可迭代的对象不是迭代器。

python 可迭代对象与迭代器

可迭代的对象一定不能是自身的迭代器。 也就是说, 可迭代的对象必须实现 __iter__ 方法, 但不能实现 __next__ 方法。另一方面, 迭代器应该一直可以迭代。 迭代器的 __iter__ 方法应该返回自身。

比如字符串“hello world”是可迭代对象,对字符串调用iter方法(it = iter("hello world"))会返回一个迭代器 it, 迭代器it有__iter__方法和__next__方法,__next__方法放回下一个元素,__iter__方法返回迭代器本身。

虽然生成器函数看起来像函数, 可是我们不能通过简单的函数调用把职责委托给另一个生成器函数.Python 新引入的 yield from 句法允许生成器或协程把工作委托给第三方完成。

从句法上看, 协程与生成器类似, 都是定义体中包含 yield 关键字的函数。 可是, 在协程中, yield 通常出现在表达式的右边( 例如, datum = yield) , 可以产出值, 也可以不产出——如果 yield关键字后面没有表达式, 那么生成器产出 None

  协程使用生成器函数定义: 定义体中有 yield 关键字。

  yield 在表达式中使用; 如果协程只需从客户那里接收数据, 那么产出的值是 None——这个值是隐式指定的, 因为 yield 关键字右边没有表达式

  协程需要预激(next(gen) before gen.send(data))

在协程中, yield 碰巧( 通常) 出现在赋值语句的右手边, 因为 yield 用于接收客户传给 .send() 方法的参数。

仅当协程处于暂停状态的时候,才能使用send()发送值,如果协程还没有激活,则需要使用next(gen)进行激活(让协程向前执行到第一个 yield 表达式, 准备好作为活跃的协程使用),或者gen.send(None).

协程获取值的方法:

  1. 在协程中使用return,协程终止后,捕获StopIteration,StopIteration.value为return的值

  2. 使用yield from,对yield from 结构来说,解释器不仅会捕获 StopIteration 异常, 还会把 value 属性的值变成 yield from 表达式的值。yield from 结构会在内部自动捕获StopIteration 异常,这种处理方式与 for 循环处理 StopIteration异常的方式一样: 循环机制使用用户易于理解的方式处理异常。

与 .__next__() 方法一样, .send() 方法致使生成器前进到下一个yield 语句。 不过, .send() 方法还允许使用生成器的客户把数据发给自己, 即不管传给 .send() 方法什么参数, 那个参数都会成为生成器函数定义体中对应的 yield 表达式的值。 也就是说, .send() 方法允
许在客户代码和生成器之间双向交换数据。 而 .__next__() 方法只允许客户从生成器中获取数据。改变了生成器的本性: 像这样使用的话,生成器就变身为协程

yield from 结构的作用:

  替代产出值的嵌套 for 循环

  yield from 的主要功能是打开双向通道, 把最外层的调用方与最内层的子生成器连接起来, 这样二者可以直接发送和产出值, 还可以直接传入异常, 而不用在位于中间的协程中添加大量处理异常的样板代码。 有了这个结构, 协程可以通过以前不可能的方式委托职责。

python 可迭代对象与迭代器

因为委派生成器相当于管道, 所以可以把任意数量个委派生成器连接在一起: 一个委派生成器使用 yield from 调用一个子生成器, 而那个子生成器本身也是委派生成器, 使用 yield from 调用另一个子生成器, 以此类推。 最终, 这个链条要以一个只使用 yield表达式的简单生成器结束; 不过, 也能以任何可迭代的对象结束。

两种方法能避免阻塞型调用中止整个应用程序的进程:
  在单独的线程中运行各个阻塞型操作
  把每个阻塞型操作转换成非阻塞的异步调用使用

from:《Fluent Python》

上一篇:python可迭代对象和迭代器和生成器


下一篇:孤荷凌寒自学python第十六天python的迭代对象