前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【python】生成器

【python】生成器

作者头像
JuneBao
发布2022-10-26 14:41:36
3010
发布2022-10-26 14:41:36
举报
文章被收录于专栏:JuneBao

天生骄傲

生成器

直接总结

  1. 创建生成器的方法
    1. 生成器表达式:(i for i in [1, 2])
    2. yield: 函数中出现yield这个函数就是生成器,函数(生成器)执行到yield时会返回yield后面的值,并暂停,知道下次被唤醒后会从暂停处接着执行
  2. 迭代生成器: 生成器实现了python迭代协议,可以使用next()for迭代
  3. 向生成器发送消息:如果yield出现在表达式右边,下一次生成器被send()唤醒时,上一个yield会接收send()发过来的消息赋值给上一个yield左边的变量。
  4. 唤醒生成器:
    1. next():唤醒生成器,让他运行到下一个yield处,返回yield出来的值
    2. send():唤醒生成器,并给生成器发送一个消息,同时让他运行到下一个yield处,返回yield出来的值,不能直接一开始就发送具体的数据
  5. 停止生成器:close()
  6. 发送异常:throw(type, val)
  7. yield from

创建与使用生成器

直接使用生成器表达式就可以快速创建一个生成器

代码语言:javascript
复制
gen = (i for i in range(10))
print(type(gen))
for i in gen:
    print(i, end=" ")
    
# <class 'generator'>
# 0 1 2 3 4 5 6 7 8 9 

生成器实现了__next__可以使用next()来获取下一个值,当然也可以使用for循环遍历

更加常见的创建生成器的方式是使用yield关键字,一个函数如果出现yield关键字这个函数就会变成生成器,当函数运行到yield时会暂停下来,”返回“一个结果,下一次唤醒生成器时,函数会从停下来的地方继续运行

代码语言:javascript
复制
def builder_demo():
    yield 0
    yield 1
    return 3


if __name__ == '__main__':
    bd = builder_demo()
    print(type(bd))
    print(next(bd))
    print(next(bd))
    print(next(bd))
    print(next(bd))
代码语言:javascript
复制
<class 'generator'>
0
1
Traceback (most recent call last):
  File "E:/python/coroutine_test.py", line 12, in <module>
    print(next(bd))
StopIteration: 3

当没有下一个元素时调用next会抛出StopIteration异常,return的值会作为异常的值

代码语言:javascript
复制
if __name__ == '__main__':
    bd = builder_demo()
    while True:
        try:
            print(next(bd))
        except StopIteration as e:
            print(f'result is {e.value}')
            break
代码语言:javascript
复制
0
1
result is 3

yield不但可以“传递出值”,也可以接收值

代码语言:javascript
复制
def builder_demo():
    news = yield 0
    print(f'news:  {news}')
    news1 = yield 1
    print(f'new1: {news1}')
    return 3


if __name__ == '__main__':
    bd = builder_demo()
    print(next(bd))
    result1 = bd.send("hello")
    print(result1)
    result2 = bd.send("hello2")
    print(result2)
代码语言:javascript
复制
0
news:  hello
1
new1: hello2
Traceback (most recent call last):
  File "E:python/coroutine_test.py", line 14, in <module>
    result2 = bd.send("hello2")
StopIteration: 3

往暂停处传递消息使用生成器的send()方法,这个方法还可以自动迭代到生成器中的下一个对象(有next())的作用。

生成器是先yield出数据,等到下一次生成器被唤醒时,才会接收send()的数据,然后再yield出下一个数据,所以不能一开始就直接调用send()发送具体的值,会抛出TypeError

代码语言:javascript
复制
TypeError: can't send non-None value to a just-started generator

应该先执行一次next()或执行一次generator.send(None),让生成器yield出数据,send(None)的作用与next()基本一样

生成器也可以停止,使用close()方法

代码语言:javascript
复制
def builder_demo():
    news = yield 0
    print(f'news:  {news}')
    news1 = yield 1
    print(f'new1: {news1}')
    yield 4
    yield 5
    return 3


if __name__ == '__main__':
    bd = builder_demo()
    # print(next(bd))
    print(bd.send(None))
    result1 = bd.send("hello")
    print(result1)
    result2 = bd.send("hello2")
    print(result2)
    bd.close()
    print(next(bd))
代码语言:javascript
复制
0
news:  hello
1
new1: hello2
4
Traceback (most recent call last):
  File "E:python/coroutine_test.py", line 20, in <module>
    print(next(bd))
StopIteration

close()之后再使用next(),会抛出StopIteration异常

除此之外,还可以向生成器发送异常

代码语言:javascript
复制
if __name__ == '__main__':
    bd = builder_demo()
    print(bd.send(None))
    bd.throw(Exception, TypeError("throw new error"))
    print(next(bd))
代码语言:javascript
复制
0
Traceback (most recent call last):
  File "E:python/coroutine_test.py", line 19, in <module>
    bd.throw(Exception, TypeError("throw new error"))
  File "E:python/coroutine_test.py", line 2, in builder_demo
    news = yield 0
TypeError: throw new error

yield from

yield from 是python3.3 PEP380 新添加的特性,它允许将一个生成器的部分操作委派给另一个生成器,除了向子生成器委派任务,yield from也可以直接作用于迭代器,将迭代器中的每个对象逐一yield出来,如:

代码语言:javascript
复制
def demo(*args, **kwargs):
    for i in args:
        for j in i:
            yield j
# 等价于
def demo(*args, **kwargs):
    for i in args:
        yield from i

上面的函数其实就是itertools.chain() 作用是将多个迭代器中的元素迭代出来

生成器嵌套

1、调用方:调用委派生成器的客户端(调用方)代码 2、委托生成器:包含yield from表达式的生成器函数 3、子生成器:yield from后面加的生成器函数

yield from 可以架设一个调用方子生成器之间的双向桥梁

代码语言:javascript
复制
final_result = {}


def calculate():
    total = 0
    nums = []
    while True:
        info = yield
        if not info:
            break
        total += info
        nums.append(info)
    # return 的值会被赋值给yield from 左边的变量
    return total, nums


def middle(key: str, gen):
    while True:
        final_result[key] = yield from gen()
        print(final_result)


def main():
    data = {
        "apple": [230, 569, 234, 776],
        "banana": [564, 213, 798, 327],
        "strawberry": [98, 76, 120, 436, 55],
        "orange": [78, 67, 345, 124]
    }
    for key, value in data.items():
        # 不要传递calculate()!
        mid = middle(key, calculate)
        mid.send(None)  # 初激
        for v in value:
            mid.send(v)
        mid.send(None)


if __name__ == '__main__':
    main()

为什么“多此一举”架设一个“桥梁”?

  • yield from 在内部处理了大量可能的异常,简化开发,提高代码安全性和可读性

生成器的作用

  1. 适用于对大量数据的处理:如果要对产生的大量数据进一步处理时,使用容器就只能先得到所有要处理的数据,存到容器,再逐个对容器中的数据遍历,再加工,最后得到目标数据,这样第一步产生的“中间数据”只用一次,但仍需大量空间储存;使用生成器可以产生一个,加工一个,节约内存,提高效率
  2. 用于协程
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-3-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 生成器
    • 直接总结
      • 创建与使用生成器
        • yield from
          • 生成器嵌套
        • 生成器的作用
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档