pyhton中的yield关键字的作用

本文介绍了Python中的yield关键字,它用于创建生成器。生成器是一种特殊的迭代器,只在迭代时按需生成值,节省内存。文章讨论了可迭代对象、生成器的概念,并通过示例解释了yield的工作原理。还涉及了如何控制生成器的消耗、使用itertools库以及迭代的内部机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

翻译自: https://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do

基本理解

为了理解yield管自己的作用,你需要先理解什么是生成器(generators).理解生成器之前,需要先理解可迭代对象(iterables)

可迭代对象(iterables)

当你创建一个list对象时,你可以逐个成员去遍历,这种逐个成员遍历读取的操作叫做迭代(iteration)

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

这里mylist就是一个可迭代对象。如果你使用列表解析(list comprehension,或翻译成列表推导),你创建一个列表的同时,也创建了一个可迭代对象

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

每一种你可以使用for…in…语法操作的对象都是可迭代对象,比如lists、strings、files…
这些可迭代对象使用起来非常方便,你可以在需要的时候任意读取使用,因为所有的数据都存在内存(memory)里。然而你并不总是希望这些数据都存在内存里,尤其是当可迭代对象的数据量非常大的时候

生成器(generators)

生成器是迭代器(iterators),一种只能迭代一次的可迭代对象。生成器不会把所有的数据都存在内存里,而是在执行时,动态生成数据(generate values on the fly)

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

这段代码和上一段相比,除了"[]“换成了”()",其他逻辑完全一样,但是这里的mygenerator已经不再是一个列表,而是一个生成器了。生成器只能被遍历一次,所以这里不能第二次执行for i in mygenerator。在第一个for…in…循环中,计算00,打印0,丢弃0 --> 计算11,打印1,丢弃1 --> 计算2*2,打印4,丢弃4

yield

yield是一个跟return用法类似的关键字,区别在于前者返回的是一个生成器

def createGenerator():
	mylist = range(3)
	for i in mylist:
		yield i*i

mygenerator = createGenerator()
print(mygenerator)
for i in mygenerator:
	print(i)

执行结果

<generator object createGenerator at 0x03662B30>
0
1
4

这个是个没啥用的例子,但是当你的函数会返回一个巨大的数据集,而且这个数据集你只会使用一次时,这种方式用起来就很方便了
为了理解yield,你必须理解当你调用你的函数时,你在这个函数里面写的代码并不会被执行,这个函数只会返回一个生成器对象。 这个听起来有点儿迷惑(a bit tricky)
然后,当遇到for代码处理生成器时,代码才会从中断的地方继续执行
现在,最难的部分:
第一次用for语句访问你的函数返回的生成器时,代码会从函数的开头一直执行到第一个yield处,返回第一次yield的值,后面的其他继续访问这个生成器的调用,将会从这个yield之后继续执行到下一次遇到yield,返回yield的值,依此执行,直到没有返回值
一旦函数执行完(逻辑上:因为循环结束或者不满足if/else条件导致的不会再执行yield操作),执行器中不会再有值

进阶操作

控制一个生成器的消耗(controlling a generator exhaustion)
>>> 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
...

说明:python3里面,用next(corner_street_atm)或者corner_street_atm.next()代替corner_street_atm.next(),其他生成器亦然
这种操作针对各种与控制资源读取类似的场景非常有用

itertools, your best friend

itertools库包含管理迭代对象的各种特殊函数。可以实现诸如复制生成器、连接两个生成器、对交织在一个list里面的数据进行分组、在不创建新的list的情况下实现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)]
理解迭代的内部机制

迭代(iteration)是一中对可迭代对象(实现了__iter__()这个魔术方法)和迭代器(实现了__next__()这个魔术方法)的操作。可迭代对象(iterable)是包含迭代器的一种python对象。迭代器(iterator)是一个让你能够遍历一个可迭代对象的python对象

更多关于for循环迭代的信息见这里
协程相关的信息见官方文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值