Python之yield语法
生成器与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语法的更多相关文章
- Python关键字yield的解释(stackoverflow)
3.1. 提问者的问题 Python关键字yield的作用是什么?用来干什么的? 比如,我正在试图理解下面的代码: def node._get_child_candidates(self, dista ...
- Python基础:语法基础(3)
本篇主要介绍Python中一些基础语法,其中包括:标识符.关键字.常量.变量.表达式.语句.注释.模块和包等内容. 1. 标识符和关键字 1.1 标识符 标识符是变量.常量.函数.属性.类.模块和包等 ...
- python 关键字yield解析
python 关键字yield解析 yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator.y ...
- 一. Python基础(1)--语法
一. Python基础(1)--语法 1. 应用程序 1.1 什么是计算机(Computer)? 组成 ①运算器 arithmetic unit; ※ Arithmetic unit and cont ...
- python语句和语法
python语句和语法 python程序结构: 1.程序由模块构成. 2.模块包含语句. 3.语句包含表达式. 4.表达式建立并处理对象. python的语法实质上是有语句和表达式组成的.表达式处理对 ...
- python 关键字yield
问题 Python 关键字 yield 的作用是什么?用来干什么的? 比如,我正在试图理解下面的代码: def node._get_child_candidates(self, distance, m ...
- Python的基础语法(二)
0. 前言 接着上一篇博客的内容,我将继续介绍Python相关的语法.部分篇章可能不只是简单的语法,但是对初学者很有帮助,也建议读懂. 1. 表达式 由数字.符号.括号.变量等组成的组合. 算术表达式 ...
- 六. Python基础(6)--语法
六. Python基础(6)--语法 1 ● Python3中, Unicode转字节的方法 print(bytes("李泉", encoding = 'utf-8')) prin ...
- 五. Python基础(5)--语法
五. Python基础(5)--语法 1 ● break结束的是它所在的循环体, continue是让它所在的循环体继续循环 # 打印: 1 10 2 10 3 10 4 10 5 10 6 10 7 ...
随机推荐
- Bad owner or permissions on .ssh/config win10问题解决
最近向系统添加了新用户账号后出现了问题,尝试使用私钥登陆服务器,提示了 Bad owner or permissions on .ssh/config 这个报错,就是如题中的问题 修复 按照Windo ...
- Arraylist的遍历方式、java反射机制
先定义ArrayList再添加几条数据: ArrayList arr=new ArrayList(); //往arrList中增加几条数据 arr.add(1); arr.add(2) ...
- Angular 学习笔记 (Material table sticky 原理)
更新 : 2019-12-03 今天踩坑了, sticky 了解不够深 refer http://www.ruanyifeng.com/blog/2019/11/css-position.html 阮 ...
- maven一些简单常用却容易记混的命令参数-U -e -B
install 命令完成了项目编译.单元测试.打包功能,同时把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库,但没有布署到远程Maven私服仓库: deploy 命令完成了项目 ...
- Python、PyCharm、Django框架安装
一.下载Python环境 1.1 下载Python环境,以下网址: https://www.python.org/downloads/release/python-373/ 下载安装包: 1.2点击安 ...
- ReLU函数的缺陷
ReLU激活功能并不完美. 它有一个被称为 “ReLU 死区” 的问题:在训练过程中,一些神经元会“死亡”,即它们停止输出 0 以外的任何东西.在某些情况下,你可能会发现你网络的一半神经元已经死亡,特 ...
- PyCharm 占用过大 C 盘空间,system 配置文件迁移
随着 PyCharm 的持续使用,对应 C:\Users\<username>\.PyCharm<2018.3> 下的文件大小会持续增大,且通常为 system 文件夹下的内容 ...
- 【转载】C#中PadLeft函数按特定字符补足字符串长度
在C#开发过程中字符串String类处理过程中,有时字符串长度不够时,需要在左侧指定特定的字符来补足字符串长度,此时可以使用String类下的PadLeft方法对字符串的左边进行按特定的字符和特定的长 ...
- MySQL基础篇
数据库基础知识 以MySQL为基础 数据库事务 :数据库中一组原子性的SQL操作,彼此状态一致.具有ACID特性. 事务 ACID 特性: 原子性:数据库事务是一个整体,其中的SQL操作要么全部提交成 ...
- JAVA笔记整理(三),JAVA中的类和方法
类 类是JAVA中一个重要的概念,可以把类理解成一个对象的抽象,这个抽象的对象包含了变量(用来描述这个对象的属性)和方法(用来描述这个对象可以干什么),类中的各个成员之间可以相互调用(static修饰 ...