pyhton中的yield关键字的作用

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

翻译自: 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循环迭代的信息见这里
协程相关的信息见官方文档

Python,`yield`关键字用于定义生成器函数。生成器函数是一种特殊的函数,它可以产生一个迭代器对象,通过迭代器可以逐步产生值而不是一次性返回所有结果。生成器函数使用`yield`语句来指示函数在每次被调用时暂停执行,并返回一个值给调用者。调用者可以通过调用生成器的`next()`方法或使用`for`循环来逐步获取生成器产生的值。 除了在生成器函数使用`yield`语句来控制生成器的执行流程外,`yield`还可以与其他语句和表达式一起使用,例如`send()`方法可以在生成器发送一个值,并将其作为`yield`表达式的结果,`throw()`方法用于在生成器引发一个异常,`close()`方法用于关闭生成器。 PEP342进一步增强了生成器函数的功能,将`yield`从一个关键字(statement)变为表达式(expression),并引入了`send()`,`throw()`,`close()`等方法来操作生成器。这些增强使得生成器函数更加灵活和功能强大。 通过使用生成器函数,我们可以按需生成序列的值,而不需要一次性生成所有的值,这样可以节省内存并提高效率。此外,生成器函数还可以用于实现协程和异步编程等高级应用。 参考文献: PEP 342 -- Coroutines via Enhanced Generators: https://www.python.org/dev/peps/pep-0342/ Real Python - Introduction to Python Generators: Using Advanced Generator Methods: https://realpython.com/introduction-to-python-generators/#using-advanced-generator-methods Real Python - Generators in Python: Creating and Iterating Simple Generators: https://realpython.com/introduction-to-python-generators/<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [python yield关键字全面解析](https://blog.csdn.net/jeffery0207/article/details/124856788)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值