Python之yield

本文介绍了Python中的yield语句,用于创建生成器。生成器在每次迭代时暂停并保存状态,避免了大量数据占用内存。以斐波那契数列为例,展示了普通函数与生成器的区别。此外,还提到了生成器的send方法,允许在运行时向生成器发送值,影响其执行流程。使用send不仅传递值,还能使生成器从yield处继续执行。
摘要由CSDN通过智能技术生成

yield语句用于生成一个迭代器。函数中使用了yield之后,函数就不再是函数了,而是一个生成器,生成一个迭代器。

迭代器的特点就是每次运行时,运行到yield语句处就停下来了,下次再调用的时候,从yield语句处往下继续执行。

以生成斐波那契数列的函数fab为例,进行解释。

传统函数实现的源码如下:


#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        print b      # 使用 yield
        # print b 
        a, b = b, a + b 
        n = n + 1
 
for n in fab(5): 
    print n

上边的例子中,fab()函数执行时,每次打印一个b值,最终打印结果是

1

1

2

3

5

把代码中的print b替换成yield b之后,fab()就成了一个生成器了,就不是一个普通的函数了


#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        yield b      # 使用 yield
        # print b 
        a, b = b, a + b 
        n = n + 1

这里的fab()就是一个生成器,fab(5)就不再是函数调用,而是生成了一个迭代器,因此我们可以将fab(5)赋值给一个变量,并对这个变量执行的迭代器的操作,如next()。

f = fab(5)
>>> next(f) #这是Python 3的写法,Python 2的写法是f.next()
1
>>> next(f)
1
>>> next(f)
2
>>> next(f)
3
>>> next(f)
5
>>> next(f)

Traceback (most recent call last): 
 File "<stdin>", line 1, in <module> 
StopIteration

每次执行f.next(),程序运行到yield b处就会返回b,并停在这里,下次再调用f.next()的时候,从上次停下来的位置继续执行后边的代码(局部变量的值保持不变),然后运行又运行至yield,返回一个b,再停下来,以此类推。

既然fab(5)现在是个迭代器,那么自然就可以用在for循环中,于是就有了下边的代码:


#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        yield b      # 使用 yield
        # print b 
        a, b = b, a + b 
        n = n + 1

for n in fab(5):
    print(n)

在上述源码中,for循环每循环一次,其实系统自动调用了一次next对fab(5)进行求值,然后通过yield返回。当迭代到头的时候,系统自动处理。

此时可能会问,这么做有什么好处?fab()函数返回一个列表行不行?为什么非要用yield把fab()弄成一个迭代器?答案是,如果把fab()函数设置成返回列表的形式,那么随着N的增大,程序所占内存会随之增大。而使用yield则不会出现这样的问题,它每执行一次返回一个值。

既然fab(5)此时已经是个生成器了,那么自然的引申到,生成器除了next()之外,还有send方法,接下来就讲讲send的用法。

把上边的fab()稍微改一下


#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        ret = yield b      # 使用 yield
        # print b 
        if ret == "break":
            print("enter break")
            break
        a, b = b, a + b 
        n = n + 1

f = fab(5)
>>>next(f)
1
>>>f.send("break")
enter break

Traceback (most recent call last):
  File "/usercode/file.py", line 16, in <module>
    f.send('break')
StopIteration

上边这段代码中,使用了语句ret = yield b,天真的你是不是以为ret的值就是b,奶义乌啊。如果不用send()函数,ret的值是None,如果用了send()函数,send()的参数就被传给了ret,注意,send()的参数不是传给b,而是ret。send()的执行过程是将参数传给ret,然后执行一次next()。所以上边f.send(''break'')的运行结果是enter break,即send不仅仅是把值传进去,还从上次yield停止的地方继续往下执行了,又因为在if ret == "break"中,执行了break语句,即跳出了while循环,fab()执行到了最后,所以触发了“StopIteration”,输出了后边的内容。

参考资料:https://blog.csdn.net/lxy210781/article/details/87539396

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值