Python强大的yield关键字

什么是生成器(Generator)

生成器:只有在被调用的时候才会产生相应的数据,它实现了内置的__next__()方法,所以它是一种特殊的迭代器
为什么要用生成器?:假如有一个list,存放着1T甚至更大的数据量,但其中的每个数据你想需要的时候再取(通俗点讲就是仅仅想要遍历这个列表)。如果你直接把它加载至内存,内存就可能会不足。生成器就是为了解决此类内存占用,使用生成器能帮助你极大地降低内存并提高开发效率。
实现生成器的两种方式

  1. 将平时用的列表生成式改为()便是一个生成器:如:(i for i in range(100))
  2. 在函数内部使用yield关键词。(只要函数内部使用了yield,它便成了一个生成器

生成器的特点

  • 本身具有与迭代器相同的特性:可以通过next() 单次取出或使用for循环一次性取出。
  • 取出数据的过程是不可逆的,且只能使用一次,生成器会被耗尽 。举例:
>>> gen = (i for i in range(10))
>>> type(gen)
<class 'generator'>   # 这是一个生成器
>>> next(gen)
0
>>> next(gen)
1
>>> list(gen)  # 这时是从2开始生成列表
[2, 3, 4, 5, 6, 7, 8, 9]
>>> list(gen)  # 此时的生成器内部的指针已到末尾,返回空
[]

yield基本用法

先来了解最基本的用法:

def f():
    print("next1")
    yield 1
    print("next2")
    yield 2
    return "生成器结束"   作用是:得到异常的报错信息 

gen = f()
print("从生成器接收到的值为:", next(gen))  """程序运行到yield 1处停止,并返回yield后面的值"""
print("从生成器接收到的值为:", next(gen))  """此时程序继续向下执行,到yield 2处结束"""

结果:

next1
从生成器接收到的值为: 1
next2
从生成器接收到的值为: 2

可以发现:yield 关键字与return很相似,都是向外提供数值。但是return会使整个函数结束,而使用yield不会,函数会终止,停留在yield处等待下一个"命令"。returnyield的关系就像是breakcontinue的关系。

此时如果再调用next(),程序会抛出异常:
StopIteration: 生成器结束
我们还可以对其进行异常处理,得到

try:
    next(gen)
except StopIteration as e:
    print(e.value)
结果:生成器结束

还可以利用生成器来生成一些有规律的值,如斐波那契数列:

def fib(max):
    lst = [0] * max
    lst[0], lst[1] = 1, 1
    for i in range(2, max):
        lst[i] = lst[i-1] + lst[i-2]  "当前的值为前两项的和"
        yield lst[i]


for num in fib(10):
    print(num, end=', ')

得到结果:
2, 3, 5, 8, 13, 21, 34, 55,

yield高级用法

当然yield的功能远不止如此,接下来是yield最牛X的功能,也是最难理解的一个部分。
先通过一段简单的代码来理解:

def gen():
    html = yield "http://www.baidu.com" # step2
    print(html)
    yield "Google"
    return 'end'


g = gen()
url = next(g)  # step1
print('将url经过下载后得到网页源码"百度"')
print(g.send('百度'))  # step3
  1. 首先生成一个生成器g,程序来到step1调用next()会得到g产生的第一个值,即 "http://www.baidu.com", 注意此时生成器的内部会来到step2,等待下次调用时从step2后开始执行。
  2. step2 的yield它实际上有两个功能 1.向外提供值 2.接收传递进来的值。
  3. 解释器继续向下运行,除了next()方法可以让生成器产生值外,生成器g本身也有个send()方法可以让它“产值”,send()方法还可以传入值,yield前的变量会接受该值,这里我们模拟一个网页的下载过程,send()又将下载到的"百度"传入到生成器内部并赋值给html,此时程序继续向下执行,来到第二个yield停止。send()方法又会收到第二个yield的值,即Google
  4. 以上如果不理解,手动尝试进行断点调试即可。

理解之后,你就应该感受到yield功能的强大了!协程就可以由yield来实现。下面来看一个生产者消费模型:

def consumer(name):
    print('%s已经准备就绪....' % name)
    for _ in range(4):
        # 1.可以产出值,2,可以接收值(调用方传递进来的值)
        fruit = yield "hello" + name  # 等待next或是send
        print('%s来了,被%s吃了' % (fruit, name))


def producer(name):
    c1 = consumer('A')
    c2 = consumer('B')
    print("nextA的返回值:", next(c1))  # 作用:让生成器走到yield并停止,等同于c1.send(None), 并接收yield发出的值
    print("nextB的返回值:", next(c2))
    print("循环开始", '-' * 100)
    for _ in range(3):
        print('%s生产了两种水果' % name)
        # 调用send发送非None值之前,必须启动一次生成器,方式有两种1.gen.send(None), 2.next(gen)
        print("sendA的返回值:", c1.send('apple')) # 作用 1.回到yield处,并给它赋值 2.跳到下一个yield并接收yield发出的值
        print("sendB的返回值:", c2.send('pear'))
        print('-' * 100)


if __name__ == '__main__':
    producer('aaa')

以上程序可以拷贝至IDE中进行断点调试,重要注释也都写了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值