生成器与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. WUSTOJ 1324: Base64 Coding(Java)未解决,求题解

    题目链接:1324: Base64 Coding 资料:ASCII码表 原文是英文,而且篇幅较长.因此下面不粘贴原文,只写中文大意. Description Base64是一种编码算法.它的工作原理是 ...

  2. 静态成员函数和(CPP与C结构体的区别)

    #include <iostream> using namespace std.; //这种写法只是CPP中的struct的用法,但是在C中还是不支持的. //C中的结构体不支持写方法的. ...

  3. typescript 入门教程四

    ts中的function和接口 interface PrintCallback{ // 匿名函數,返回类型为空 (success:boolean):void } interface Person{ / ...

  4. Java版Kafka使用及配置解释

    Java版Kafka使用及配置解释 一.Java示例 kafka是吞吐量巨大的一个消息系统,它是用scala写的,和普通的消息的生产消费还有所不同,写了个demo程序供大家参考.kafka的安装请参考 ...

  5. 多线程使用libcurl

    curl默认情况下有两个地方是线程不安全的, 需要特殊处理, 1是curl_global_init 这个函数必须单线程调用, 2是默认多线程调用https会莫名其妙的挂掉, 以下是网上的解决方案 ht ...

  6. mysql8.0入坑体验

    正常从官网下载,并且正常安装,直到安装完成.然后用navicate连接,发现报错信息如下所示Client does not support authentication protocol reques ...

  7. 用D3js的区域生成器实现简单波浪图

    最近做控件遇到含有波浪图的图表,一开始用Echarts虽然很快完成了,但Echarts的波浪图与其他图表的响应式不同步,于是学习了D3js,D3js写起来确实复杂一些,但能够实现的效果也更丰富,做的时 ...

  8. JavaScript时钟效果

    在JavaScript中,有一个内置对象Date,它重要的一个作用就是实现了时间的时刻更新,通过代码来创造一个实实在在的时间表. 代码例子: <!DOCTYPE html> <htm ...

  9. IntelliJ IDEA.2017.3.4(win7 64位)的安装使用

    下载 1.Idea与Webstorm同为JetBrains公司的产品,因此安装使用方式也极为相似,首先我们打开IDEA的官方下载网站:https://www.jetbrains.com/idea/,点 ...

  10. nodejs 模块全局安装路径配置

    nodejs下载安装完成后 输入npm config ls 或者npm config list npm 默认的全局安装路径为该路径,将包都下载在C盘中不是我们想要的结果.一般建议修改在nodejs的安 ...