生成器与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. leetcode动态规划笔记三---单序列型

    单序列型DP 相比一维DP,这种类型状态转移与过去每个阶段的状态都有关. Longest Increasing Subsequence : 求最大最小值 Perfect Squares : 求某个规模 ...

  2. Python内存加载shellcode

    生成 首先生成一个测试的msf shellcode msfvenom -p windows/x64/exec CMD=calc.exe -f python 把其中的shellcode复制出来留待待会使 ...

  3. Springboot模板(thymeleaf、freemarker模板)

    目的: 1.thymeleaf模板 2.Freemarker模板 thymeleaf模板 thymeleaf 的优点: 支持html5标准,页面无须部署到servlet开发到服务器上,直接通过浏览器就 ...

  4. centos7安装oracle11g(根据oracle官方文档安装,解决图形界面安装问题)

    一.系统及安装包 操作系统:centos 7.4 oracle版本:oracle 11g r2 二.centos环境配置 安装数据库所需要的软件包 [root@localhost data]# yum ...

  5. Generator 实现

    Generator 是 ES6 中新增的语法,和 Promise 一样,都可以用来异步编程 // 使用 * 表示这是一个 Generator 函数 // 内部可以通过 yield 暂停代码 // 通过 ...

  6. C++开源库大全

        标准库 C++ Standard Library:是一系列类和函数的集合,使用核心语言编写,也是C++ISO自身标准的一部分. Standard Template Library:标准模板库 ...

  7. 1+x证书学习日志——css 基本选择符

    ##css选择符                 1:类型选择符 直接用标签名称当作选择符                     特点:选中所有同类元素                 2:id名称 ...

  8. Go数据类型之基本数据类型

    不想沦为芸芸众生的人只需做一件事,便是对自己不再散漫:他应当听从良知的呼唤:“成为你自己!” ---尼采 1.整型 有符号整数类型:int8.int16.int32和int64 无符号整数类型:uin ...

  9. python自定义ORM并操作数据库

    看这个代码之前先去看上篇文章,理解type的用法及元类的含义: ORM可以代替pymysql,实现将python语义装换为sql语句,简单化 import pymysql ''' metaclass, ...

  10. subprocess、struct模块的简单应用与ssh模型(黏包)

    一.subprocess模块 #可以通过传递字符串命令,帮你去实现一些操作系统的命令. import subprocess res = subprocess.Popen("dir" ...