python中的yield

本文深入浅出地介绍了Python中yield关键字的作用与用法,并通过实例详细解析了生成器的概念及其与迭代的区别。

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

其实一开始并不想学这个用法。但是随着教材往下看,总是能碰到yield这个东西,导致什么都看不下去。神烦的不行。于是专门拿出来啃一下

http://pyzh.readthedocs.org/en/latest/the-python-yield-keyword-explained.html 

这是一篇从stackflow翻译过来的答案, 很好的解释了yield。虽是中文,不过我看的时候还是很吃力的,初学者都这样吧........哎.......

逻辑上来看,想弄懂yield就要懂生成器generator,想懂generator就要懂迭代iteration。

(不行,我要吐槽一下,好多网上搜到的资料都是给有一定编程基础的人写的,解释一个命令,就会带出另外一个没见过的命令或者概念,嵌套嵌套再嵌套,没完没了。。。举例:本来想查一下enumerate的用法,结果碰到yield,查yield,结果碰到xrange,更别提遇到各种带下划线的奇怪函数....真是够了 嗷嗷嗷)

吐槽归吐槽,继续硬着头皮学吧。。。


---------------------------------------------------------------------------------------------------------------------

注意:以下内容是上面链接的解释版。如果看了链接可以不看这段

[python]  view plain  copy
  1. mylist = [x*x for x in range(3)]  
  2. print mylist  
  3. mygen=(x*x for x in range(3))  
  4. print mygen  


代码解释

行1生成一个列表list,这个列表的每个元素由x的平方组成,x的取值为range(3),也就是0,1,2。因此这个列表有三个元素:0的平方(0),1的平方(1),2的平方(4)。

行2打印出这个列表的内容,显示结果果真是[0,1,4]

行3生成的是一个生成器generator,它和行1唯一的不同就是它用的小括号。但是产生的返回值并不再是一个列表了。那是什么呢?

行4想要打印出来这个mygen生成器,但结果显示是这样的: <generator object <genexpr> at 0x022F8030> 一个内存地址。


怎么理解这个生成器的概念呢?如上例子所示,其实这个mygen生成器就是用来生成x的平方的东西。这个结果呢就存在上面显示的内存地址里。但是由于你还没说你到底要谁的平方,所以只能看到个地址不能看到答案。mylist则不同,它是把所有答案穷举列在内存里了,你需要哪个就从里面找出来即可,比较耗费资源。而mygen则还没生成,你需要哪个我现制造一个出来放在一个内存空间显示,节省了资源。(好吧这一段都是我自己的理解....教科书还没有看....)


辣么,怎么用这个生成器涅?举例

[python]  view plain  copy
  1. mygen=(x*x for x in range(3))  
  2. for i in mygen:  
  3.     print (i)  

这样就可以把mygen可以生成的所有平方数拿出来了。结果显示:

0

1

4

注意,答案是一个一个蹦出来的,就好像循环读取一样。辣么,当你需要循环读取、而不是一下子全显示出来的时候,用生成器是最好不过的了。

---------------------------------------------------------------------------------------------------------------

如果看上一段内容还是比较困难的话,我们再来看下面这段。我找了个新guide,可见: www.cnpythoner.com/post/298.html

---"在python中,当你定义一个函数,使用了yield关键字时,这个函数就是一个生成器" (也就是说,只要有yield这个词出现,你在用def定义函数的时候,系统默认这就不是一个函数啦,是一个生成器啦!!不管里面内容你写成神马样子 )

--- 一般def定义的函数,都会return一个返回值。而def定义的生成器,返回的则是一个对象,也就是上面提到的类似于“内存地址”的东西。(看来我上面解释的还不是很对)

--- 如果需要生成器返回(下一个)值,需要调用.next()函数。其实当系统判断def是生成器时,就会自动支持.next()函数

[python]  view plain  copy
  1. c = h() #h()包含了yield关键字    
  2. print  c.next() #返回值   

----------------------------------------------------------------------------------------------------------------

辣么,我们来做一个小小的习题,看看是否理解了yield

----------------------------------------------------------------------------------------------------------------

[python]  view plain  copy
  1. def fib(max):  
  2.     a, b = 11  
  3.     while a < max:  
  4.         yield a  
  5.         a, b = b, a+b  
  6.   
  7. for n in fib(15):  
  8.     print n  
  9.   
  10. m = fib(13)  
  11. print m  
  12. print m.next()  
  13. print m.next()  
  14. print m.next()  

以上是一段关于斐波那契数列的程序。

fib()函数因为含有yield,被系统默认为是一个生成器。

for语句调用了fib(15)。当max=15时,进入fib()生成器,执行到yield a, 返回a值以及整个生成器暂停的状态,将a值赋给n, 打印出来;因为是for语句循环,所以又回到fib(15)语句,由于是生成器,因此从上次截断的位置开始执行,b值赋给a, a+b值赋给b,又因为是while语句,则继续while循环,yield a值,循环暂停跳出返回a值及生成器状态,把a值赋给n, 打印n。如此往复,一直循环到15结束。

m被赋了fib(13)这个生成器,每一次执行m.next()函数就会打印下一个值。

从上面的分析过程,我们看一下运行结果:



注意:

1. 每个生成器只能使用一次。比如上个例子中的m生成器,一旦打印完m的6个值,就没有办法再打印m的值了,因为已经吐完了。

2. yield一般都在def生成器定义中搭配一些循环语句使用,比如for或者while,以防止运行到生成器末尾跳出生成器函数,就不能再yield了。有时候,为了保证生成器函数永远也不会执行到函数末尾,会用while True: 语句,这样就会保证只要使用next(),这个生成器就会生成一个值,是处理无穷序列的常见方法。

<think>嗯,用户想了解Pythonyield的用法和生成器的相关知识。首先,我需要回顾一下生成器的基本概念。生成器是一种特殊的迭代器,可以通过函数来创建,而yield关键字就是用来定义生成器的关键。用户可能已经知道普通的函数使用return返回值,但yield的不同之处在于它会暂停函数执行并保留状态,下次调用时继续执行。 接下来,我需要解释yield的工作流程。例如,当函数执行到yield语句时,会返回一个值,但不会退出函数,而是保持当前状态。下次调用next()时,函数从yield之后继续执行。这点可能需要用例子来说明,比如斐波那契数列的生成器,这样用户能更直观地理解。 然后,生成器的优势是什么?内存效率高,因为不需要一次性生成所有数据,而是按需生成。这在处理大数据时特别有用。比如,当用户需要处理一个很大的文件或者数据集时,生成器可以逐个产生数据,而不是全部加载到内存中。这可以引用用户提供的引用内容中的例子,比如sum(firstn(1000000)),说明生成器如何提升性能。 另外,用户可能想知道生成器的应用场景。比如流式处理、惰性计算、协程等。这些场景需要展开说明,让用户了解实际使用中的情况。比如,使用生成器处理日志文件,逐行读取处理,而不是一次性读入内存。 用户提供的引用中有提到生成器和迭代器的区别,可能需要对比说明。生成器是迭代器的一种,但更简洁,不需要实现__iter__和__next__方法,只需使用yield即可。引用中的斐波那契例子可以再次使用,展示生成器的简洁性。 还要注意yield的常见误区,比如函数中使用yield后,函数就变成了生成器函数,调用时返回生成器对象,而不是直接执行。这点需要强调,避免用户误解。同时,生成器只能遍历一次,之后需要重新创建,这也是需要注意的地方。 用户的问题可能涉及如何创建生成器,除了函数中使用yield,还可以用生成器表达式。比如(i for i in range(10)),和列表推导式类似,但使用圆括号。这点可能需要补充说明,以全面回答用户的问题。 最后,结合用户提供的引用内容,引用相关的部分来支持解释,比如引用[3]提到的yield类似return,但函数不会释放状态,这点需要强调。同时,引用[2]中的例子可以说明生成器在性能上的优势,比如处理大量数据时节省内存。 总结来说,回答的结构应该包括:生成器的定义、yield的工作机制、生成器的优势、应用场景、与迭代器的对比、创建方式以及注意事项。通过例子和引用中的内容来增强解释的可信度,并确保语言通俗易懂,帮助用户逐步理解。</think>### Python yield 用法及生成器详解 #### 一、生成器的核心概念 生成器是一种特殊的迭代器,通过函数中的`yield`关键字实现。与普通函数使用`return`不同,`yield`会**暂停函数执行并保留当前状态**,下次调用时从暂停处继续执行[^3]。 #### 二、yield 的工作机制 1. **执行流程** - 当函数执行到`yield`时,返回右侧表达式的值 - 函数状态(变量值、执行位置)被冻结 - 下次调用`next()`时,从`yield`下一行继续执行 ```python def count_down(n): while n > 0: yield n n -= 1 gen = count_down(3) print(next(gen)) # 输出 3 print(next(gen)) # 输出 2 ``` 2. **状态保持特性** 生成器函数中的局部变量在多次调用中会保持状态,而普通函数每次调用都会重新初始化变量[^3]。 #### 三、生成器的核心优势 1. **内存效率** 按需生成数据,避免一次性加载所有数据到内存。例如处理百万级数据: ```python def large_dataset(): for i in range(1_000_000): yield i * 2 ``` 此生成器仅需几十字节内存,而列表需要约8MB[^2]。 2. **惰性计算特性** 数据只有在被请求时才会生成,适合处理: - 无限序列(如斐波那契数列) - 流式数据(如日志文件实时处理) - 大数据集分块处理 #### 四、典型应用场景 1. **高效迭代器实现** ```python def fibonacci(): a, b = 0, 1 while True: yield a a, b = b, a + b fib = fibonacci() print([next(fib) for _ in range(10)]) # 输出前10项 ``` 2. **协程实现基础** `yield`可实现双向通信,是协程编程的基础: ```python def coroutine(): while True: received = yield print(f"Received: {received}") c = coroutine() next(c) # 启动协程 c.send("Hello") # 输出 Received: Hello ``` #### 五、生成器进阶用法 1. **表达式形式** 使用圆括号创建生成器表达式: ```python squares = (x*x for x in range(10)) sum(squares) # 返回 285 ``` 2. **数据管道构建** 多生成器串联实现复杂处理流程: ```python def read_lines(file): with open(file) as f: for line in f: yield line.strip() def filter_comments(lines): for line in lines: if not line.startswith('#'): yield line pipeline = filter_comments(read_lines('config.ini')) ``` #### 六、注意事项 1. **一次性特性** 生成器遍历完成后需要重新创建: ```python gen = (x for x in range(3)) list(gen) # [0,1,2] list(gen) # [] ``` 2. **异常处理** 使用`generator.close()`或`generator.throw()`管理生成器生命周期。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值