Python yield 关键字有什么作用?详细解答

yield 关键字有什么作用?要了解 yield 的作用,您必须了解生成器是什么。在了解生成器之前,您必须了解iterables(可迭代对象)。

1. 什么是可迭代对象?

对于列表时,您可以一项一项地输出它的值。一项一项地读取列表的内容,这种形式被称为迭代:

>>> mylist = [1, 2, 3]
>>> for i in mylist:
...    print(i)
1
2
3

mylist是一个可迭代的对象。当您使用列表推导式时,您将创建一个列表,以及一个可迭代对象:

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...    print(i)
0
1
4

那些可以被你使用 “ for... in...” 迭代的所有对象都是可迭代的,比如 数组、字符串等。

这些可迭代对象很方便,因为您可以随心所欲地读取它们,但是您将所有值存储在内存中,当您有很多值时,这并不总是您想要的。

2. 什么是生成器?

生成器是迭代器,一种只能迭代一次的可迭代对象。生成器不会将所有值存储在内存中,它们会即时生成值

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...    print(i)
0
1
4

生成器创建的时候需要用 () 而不是 [] . 但是,您不能重复执行 for i in mygenerator ,因为生成器只能使用一次:它们计算出 0 (0*0),然后忘记它并计算得到 1 (1*1),然后一一结束计算得到 4 (2*2)。

3. 重点来了,什么是yield? yield 关键字有什么作用?

yield 是一个像 return 一样使用的关键字,不同的是使用yield会使该函数返回一个生成器。

>>> def create_generator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
>>> mygenerator = create_generator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object create_generator at 0xb7555c34>
>>> for i in mygenerator:
...     print(i)
0
1
4

这是一个无用的示例,但是当您知道您的函数将返回大量您只需要读取一次的值时,它会很方便。

要掌握yield,你必须明白,当你调用函数时,你写在函数体中的代码并没有运行。该函数只返回生成器对象。然后,您的代码将在每次for循环使用生成器时从停止的地方继续。

现在是困难的部分:

第一次 for 调用从您的函数创建的生成器对象时,它将从头开始运行您的函数中的代码,直到命中yield,然后它将返回循环的第一个值。然后,每个后续调用将运行您在函数中编写的循环的另一次迭代并返回下一个值。这将一直持续到生成器被认为是空的为止。


4. 控制生成器耗尽的一个例子

>>> class Bank(): # Let's create a bank, building ATMs
...    crisis = False
...    def create_atm(self):
...        while not self.crisis:
...            yield "$100"
>>> hsbc = Bank() # When everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # Crisis is coming, no more money!
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # It's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # Build a new one to get back in business
>>> for cash in brand_new_atm:
...    print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...

注意:对于 Python 3,使用print(corner_street_atm.__next__())print(next(corner_street_atm))

它可用于控制对资源的访问等各种事情。

5.Itertools,你最好的朋友

itertools 模块包含操作可迭代对象的特殊函数。曾经想复制一个生成器吗?连接两个生成器?使用单行对嵌套列表中的值进行分组?Map/Zip 不创建另一个列表?

那么就 import itertools.

一个例子?让我们看看四马比赛可能的到达顺序:

>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
 (1, 2, 4, 3),
 (1, 3, 2, 4),
 (1, 3, 4, 2),
 (1, 4, 2, 3),
 (1, 4, 3, 2),
 (2, 1, 3, 4),
 (2, 1, 4, 3),
 (2, 3, 1, 4),
 (2, 3, 4, 1),
 (2, 4, 1, 3),
 (2, 4, 3, 1),
 (3, 1, 2, 4),
 (3, 1, 4, 2),
 (3, 2, 1, 4),
 (3, 2, 4, 1),
 (3, 4, 1, 2),
 (3, 4, 2, 1),
 (4, 1, 2, 3),
 (4, 1, 3, 2),
 (4, 2, 1, 3),
 (4, 2, 3, 1),
 (4, 3, 1, 2),
 (4, 3, 2, 1)]

6.理解迭代的内部机制

迭代是一个包含可迭代对象(实现__iter__()方法)和迭代器(实现__next__()方法)的过程。

可迭代对象是您可以从中获取迭代器的任何对象。迭代器是让你迭代可迭代对象的对象。

这篇文章中有更多关于for循环如何工作的内容。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注