Python关键字yield学习

文章目录
  1. 1. 1.生成常见用法:循环迭代 or 递归
  2. 2. 2.生成器方法
  3. 3. 3.throw()与close()中断Generator

yield,英文意思是“出产,产生,收益”。所以导致包含该关键字的函数叫“生成器”?

在学习python之前,我曾看到过有其他语言使用过,但我所用的C++和Java里并没有这个关键字。所以对于我而言这是一全新的内容,这样的内容对于我这样的python初学者而言,自然需要尤其重视。

读完一些资料上关于yield的内容,其实yield的功能就是“暂停”当前函数,这个“暂停”可以让一些代码变得“神奇&优美”!

1.生成常见用法:循环迭代 or 递归

对于较小的文件,当然可以一次载入所有数据到内存里,慢慢处理,但是,如果文件太大,那么一次载入所有数据是非常低效率甚至不可行的。这时就利用yield生成器的概念,每次加载处理一部分,下次迭代接着处理。

看下面一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def lines(file):
for line in file: yield line
yield '\n'
def blocks(file):
block = []
for line in lines(file):
if line.strip():
block.append(line)
elif block:
yield ''.join(block).strip()
block = []
for block in blocks(sys.stdin):
pass

以上代码,利用生成器,每次处理一段文本块里的一行,优雅高效的遍历了整个文件。

实际上,任何包含yield语句的函数称为生成器。这些函数与普通函数的区别就在于,它不像return那样返回值,而是每次产生值。

每次处理到yield处,产生一个值,函数就会被冻结:即函数停在那点等待被激活。因为这种特性,生成器被广泛用于迭代各种“数据集合”。

1
2
3
4
5
6
7
8
9
nested = [[1, 2], [3, 4], [5]]
def flatten(nested):
for sublist in nested:
for element in sublist:
yield element
for num in flatten(nested):
print num

2.生成器方法

在python2.5后,生成器有了新属性,可以和“外部世界”进行交流。此时生成器作为表达式使用,返回一个值。

1
2
3
4
5
6
7
8
9
def repeater(value):
while True:
new = (yield value)
if new is not None:
value = new
r = repeater(42)
r.next()
r.send("Hello, world!")

next()方法调用,yield语句处返回None

send()方法调用后,原本暂停的函数repreater()会在yield语句处返回send发送的值“Hello,world!”。

关于next(),也是有返回值的

1
2
3
4
5
6
7
8
def test():
print 'greed coder'
m = yield 123
print 'Greed is good!', m
ret = test()
a = ret.next()
print 'Just for test a=', a, '.'

结果:

greed coder
Just for test a= 123 .

可见,next()是有返回值的,它的返回值很特殊,返回的是下一个yield表达式的参数。比如yield 5,则返回 5 。

3.throw()与close()中断Generator

除了send外,还有throw方法(在生成器内部引发一个异常)和close方法(停止生成器)可以被“外部“调用控制生成器的中断。
中断Generator是一个非常灵活的技巧,可以通过throw抛出一个GeneratorExit异常来终止Generator。Close()方法作用是一样的,其实内部它是调用了throw(GeneratorExit)的。

1
2
3
4
5
6
7
def close(self):
try:
self.throw(GeneratorExit)
except (GeneratorExit, StopIteration):
pass
else:
raise RuntimeError("generator ignored GeneratorExit")

因此,当我们调用了close()方法后,再调用next()或是send(msg)的话会抛出一个异常。

还听说可以将生成器转换为简单的协同(coroutine)程序,无奈我查阅的资料里一笔带过,暂时留着下次再总结进来吧~


版权声明:

本文由greedcoder创作、发表并维护,

采用自由分享-保留署名-非商用-禁止演绎4.0(CC BY-NC-ND 4.0)国际许可协议进行许可.

版权有一点,侵权不一定究!

本文永久链接:https://greedcoder.github.io/2016/08/22/python-yield/