生成器与yield

函数使用yield关键字可以定义生成器对象。生成器是一个函数。它生成一个值的序列,以便在迭代中使用,例如:

 def countdown(n):
print('倒计时:%s' % n)
while n > 0:
yield n
n -= 1
return c = countdown(10)

如果调用该函数,就会发现其中的代码不会开始执行,相反它会返回一个生成器对象,接着该生成器对象就会在__next__()被调用时执行函数。

print(c.__next__())
print(c.__next__())
print(c.__next__())

调用__next__()时,生成器函数将开始执行语句,知道遇到yield语句为止。yield语句在函数执行停止的地方生成一个结果,直到再次调用next()。然后继续执行yield()之后的语句。通常不会在生成器上直接调用next()方法,而是通过for语句、sum()或一些消耗序列的其他操作使用生成器。例如:

for i in countdown(10):
print(i) a = sum(countdown(10))
print(a)

生成器函数完成的标志是返回或引发StopIteration异常,这标志着迭代的结束。如果生成器在完成时返回None以外的值都是不合法的。生成器使用时存在一个棘手的问题,即生成器函数仅被部分消耗,例如:

for n in countdown(10):
if n == 2:
break
print(n)

在这个例子中,通过调用break退出循环,而相关的生成器也没有全部完成。为了处理这种情况,生成器对象提供方法close()标识关闭。不再使用或删除生成器时,就会调用close()方法。通常不必手动调用close()方法,但也可以这么做。

在生成器函数内部,在yield语句上出现GeneratorExit异常时就会调用close()方法。也可以选择捕捉这个异常,以便执行清理操作

 def countdown2(n):
print('倒计时:%s' % n)
try:
while n > 0:
yield n
n -= 1
except GeneratorExit:
print('GeneratorExit %s' % n) c = countdown2(2) print(next(c))
print(next(c))
del c

虽然可以捕捉GenratorExit异常,但对于生成器函数而言,使用yield语句处理异常并生成另一个输出值是不合法的。另外,如果程序当前正在对生成器进行迭代,不应该通过另一个的执行线程或从信号处理程序异步调用该生成器上的close()方法。

协程与yield表达式

在函数内, yield语句还可以作为表达式使用,出现在赋值运算符的右边,例如:

def receive():
print('Ready to receive')
while True:
n = yield
print('Got %s' % n)

以这种方式使用yield语句的函数称为协程,向函数发送值时函数将执行。它的行为也十分类似于生成器

r = receive()
r.__next__()
r.send(1)

在这个例子中,一开始调用__next__()是必不可少的,这样协程才能执行第一个yield表达式之前的语句。这时,协程会挂起,等待相关生成器对象r的send()方法给他发送一个值。

传递给send()的值由协程中的yield表达式返回。接收到值后,协程就会执行语句,直到遇到下一条yield语句。

在协程中需要调用next()这件事很容易被忽略,这经常称为错误出现的原因。因此,建议使用一个能够自动完成该步骤的装饰器来包装协程。

 def coroution(func):
def start(*args, **kwargs):
g = func(*args, **kwargs)
g.__next__()
return g
return start # 使用这个装饰器就可以像下面这样编写和使用协程:
@coroution
def receive():
print('Ready to receive')
while True:
n = yield
print('Go %s' % n) r = receive()
r.send('hello world') # 无需初始调用.next()方法

协程一般会不断地执行下去,除非被显式关闭或者自己退出。关闭后如果继续给协程发送值就会引发StopIteration异常。正如前面关于生成器的内容中讲到的那样,close()操作将在协程内部引发GeneratorExit异常。

可以使用throw(exctype [, value [.tb]])方法在协程内部引发异常,其中exctype是指异常类型,value是指异常的值,而tb是指跟踪对象例如:

r.throw(RuntimeError, "You're hosed")

以这种方式引发的异常将在协程中当前执行的yield语句处出现。协程可以选择捕捉异常并以正确方式处理它们。使用throw()方法作为给协程的异步信号并不安全--永远都不应该通过单独的执行线程或信号处理程序调用这个方法。

如果yield表达式中提供了值,协程可以使用yield语句同时接收和发出返回值,例如:

def line_splitter(delimiter=None):
print("Ready to split")
result = None
while True:
line = yield result
result = line.split(delimiter) l = line_splitter(',')
l.__next__()
print(l.send("a,b,c"))

首个__next__()调用让协程向前执行到yield result,这将返回result的值None。在接下来的send()调用中,接收到的值被放在line中并拆分到result中。

send()方法的返回值就是传递给下一条yield语句的值。换句话说,send()方法的返回值来自下一个yield表达式,而不是接收send()传递的值的yield表达式。

如果协程返回值,需要小心处理使用throw()引发的异常。如果使用throw()在协程中引发一个异常,传递给协程中下一条yield语句的值将作为throw()
方法的结果返回。如果需要这个值却又忘记保存它,它就会消失不见。

yield from

yield from 是在Python3.3才出现的语法,后面需要加的是可迭代对象。

a = 'qwertt'

def str_to_list():
yield from a def str_to_list2():
for i in a:
yield i print(list(str_to_list()))
print(list(str_to_list2()))

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

双向通道: 调用方通过send()直接发送信息给子生成器,而子生成器yield的值,也直接返回给调用方

从Python 3.5开始引入了新的语法 async 和 await ,而await替代的就是yield from

Python之yield语法的更多相关文章

  1. Python关键字yield的解释(stackoverflow)

    3.1. 提问者的问题 Python关键字yield的作用是什么?用来干什么的? 比如,我正在试图理解下面的代码: def node._get_child_candidates(self, dista ...

  2. Python基础:语法基础(3)

    本篇主要介绍Python中一些基础语法,其中包括:标识符.关键字.常量.变量.表达式.语句.注释.模块和包等内容. 1. 标识符和关键字 1.1 标识符 标识符是变量.常量.函数.属性.类.模块和包等 ...

  3. python 关键字yield解析

    python 关键字yield解析 yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator.y ...

  4. 一. Python基础(1)--语法

    一. Python基础(1)--语法 1. 应用程序 1.1 什么是计算机(Computer)? 组成 ①运算器 arithmetic unit; ※ Arithmetic unit and cont ...

  5. python语句和语法

    python语句和语法 python程序结构: 1.程序由模块构成. 2.模块包含语句. 3.语句包含表达式. 4.表达式建立并处理对象. python的语法实质上是有语句和表达式组成的.表达式处理对 ...

  6. python 关键字yield

    问题 Python 关键字 yield 的作用是什么?用来干什么的? 比如,我正在试图理解下面的代码: def node._get_child_candidates(self, distance, m ...

  7. Python的基础语法(二)

    0. 前言 接着上一篇博客的内容,我将继续介绍Python相关的语法.部分篇章可能不只是简单的语法,但是对初学者很有帮助,也建议读懂. 1. 表达式 由数字.符号.括号.变量等组成的组合. 算术表达式 ...

  8. 六. Python基础(6)--语法

    六. Python基础(6)--语法 1 ● Python3中, Unicode转字节的方法 print(bytes("李泉", encoding = 'utf-8')) prin ...

  9. 五. Python基础(5)--语法

    五. Python基础(5)--语法 1 ● break结束的是它所在的循环体, continue是让它所在的循环体继续循环 # 打印: 1 10 2 10 3 10 4 10 5 10 6 10 7 ...

随机推荐

  1. docker xfs卡死

    原因 docker在xfs文件系统中,过于频繁create/destory container.pull/push image,当thin pool满时,DeviceMapper后端默认文件系统xfs ...

  2. try except 异常捕获的方法、断言的使用

    except as e中的'e'的作用总结 - 2puT - CSDN博客 Python使用try except处理程序异常的三种常用方法分析 Python3和Python2 异常处理except的不 ...

  3. Aop 打印参数日志时,出现参数序列化异常。It is illegal to call this method if the current request is not in asynchron

    错误信息: nested exception is java.lang.IllegalStateException: It is illegal to call this method if the ...

  4. net技术方案

    https://www.cnblogs.com/qyq0323/p/11765243.html https://yq.aliyun.com/articles/300527

  5. Java 之 字符输入流[Reader]

    一.字符输入流 java.io.Reader 抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中. 它定义了字符输入流的基本共性功能方法. public void close() :关 ...

  6. django_rest framework 接口开发(二)

    1 a. 认证 - 仅使用: from django.views import View from rest_framework.views import APIView from rest_fram ...

  7. GNU/Linux 介绍

    在了解Linux之前要先了解什么是GNU / GNU官方解释? GNU是一个自由软件操作系统.就是说,它尊重其使用者的自由.GNU操作系统包括GNU软件包(专门由GNU工程发布的程序)和由第三方发布的 ...

  8. DNS服务——正向查找区 和 逆向查找区

    前言 正向查找区,就是我们最熟知的DNS.即根据域名解析成IP 逆向查找区,即根据IP解析成域名. 他们之间的关系很像ARP和RARP 正向查找区 /etc/named.rfc1912.zones用于 ...

  9. [转]memcached对key和value的限制 memcached的key最大长度和Value最大长度

    转自: 老白干之再干 memcached的简单限制就是键(key)和item的限制.最大键长为250个字符.可以接受的储存数据不能超过1MB,因为这是典型slab 的最大值.这里我们可以突破对key长 ...

  10. Google的三大马车

    Google的三大马车Google fs + Map Reduce + Big Table 开源Java实现HDFS Hadoop Hbase 云盘实现用廉价的服务器提供与万级的数据库存储①廉价的服务 ...