zoukankan      html  css  js  c++  java
  • 生成器

    通过列表推导式,我们可以生成一个列表。受内存限制,列表容量有限。当创建的列表很大,而我们只取其中几个元素,就会造成很大的内存浪费。

    为此生成器 generator 应运而生,在循环过程中它内部以某种算法不断计算出下一个元素。它不会一次性把所有元素列举出来,而是在使用的时候才会计算下一个元素,这样就节省了大量的空间。

    生成器分为两类:

    • 生成器函数:yield 关键字
    • 生成器表达式:类似于列表推导式,但是最外层为圆括号

    生成器函数

    yield 语句返回函数结果的函数即 —— 生成器函数

    普通函数与生成器函数的区别:

    • 普通函数顺序执行,遇到 return 或者最好一行就会返回
    • 生成器函数,在每次调用 next() 时执行,并遇到 yield 返回,再次执行从上次 yield 语句出执行
    def foo():
        print('第一次打印')
        yield '第一次中断'
        print('第二次打印')
        yield '第二次中断'
    
    f = foo()
    print(f)        # <generator object foo at 0x000001DEF9121DB0>
    print(next(f))
    print(next(f))
    print(next(f))
    

    函数从上到下执行,遇到 yield 返回:

    第一次打印
    第一次中断
    第二次打印
    第二次中断
    ---------------------------------------------------------------------------
    StopIteration                             Traceback (most recent call last)
    <ipython-input-99-927644374884> in <module>()
          7 print(next(f))
          8 print(next(f))
    ----> 9 print(next(f))
    
    StopIteration: 
    

    一般我们都会使用 for 循环来遍历生成器对象:

    for i in f:
        print(i)
    
    # 第一次打印
    # 第一次中断
    # 第二次打印
    # 第二次中断
    

    若想获取生成器函数 return 返回值,那么就需要手动触发 StopIteration,因为 return 返回值包含在 StopIterationvalue 中:

    def foo():
        print('第一次打印')
        yield '第一次中断'
        print('第二次打印')
        yield '第二次中断'
        return '生成器返回值'
        
    f = foo()
    
    while True:
        try:
            x = next(f)
            print('f', x)
        except StopIteration as e:
            print('生成器 return 返回值:', e.value)
            break
    
    第一次打印
    f 第一次中断
    第二次打印
    f 第二次中断
    生成器 return 返回值: 生成器返回值
    

    示例

    用生成器函数实现斐波拉契数列

    # max 为最大个数
    def fib(max):
        n, a, b = 0, 0, 1
        while n < max:
            yield b
            a, b = b, a + b
            n = n + 1
    f = fib(6)
    
    for i in f:
        print(i)        # 1、1 、2 、3 、5 、8
    

    生成器表达式

    创建一个生成器表达式很简单,只需将列表推导式最外层的中括号 [],换成圆括号 () 即可。

    l = [x*x for x in range(3)]     # 列表推导式
    
    g = (x*x for x in range(3))     # 创建生成器表达式:生成器对象
    print(l)    # [0, 1, 4]
    
    print(g)    # <generator object <genexpr> at 0x000001DEF9121F10>
    

    通过 next() 获取生成器中每个元素:

    next(g)     # 0
    next(g)     # 2
    next(g)     # 4
    next(g)     # 超出边界,触发 StopIteration
    ---------------------------------------------------------------------------
    StopIteration                             Traceback (most recent call last)
    <ipython-input-92-e9fc50c01225> in <module>()
          6 next(g)
          7 next(g)
    ----> 8 next(g)
    
    StopIteration: 
    

    一般都不会使用 next() 函数调用生成器下一个元素,而是使用 for 循环,这样也不会触发 StopIteration

    for i in g:
        print(i)    # 0/1/4
    

    总结

    • 生成器实现了迭代器协议,它是可迭代对象
    • 可以左右 for 循环,也可以使用 next() 函数调用,一次只能取一个
    • 分为生成器函数(yield)和生成器表达式
    • 生成器函数遇到 yield 即返回,下一次调用 next(),从上次 yield 处继续执行
    • 节省内存空间
  • 相关阅读:
    Python爬虫入门教程 43-100 百思不得姐APP数据-手机APP爬虫部分
    也谈PostgreSQL的Vacuum机制及其最佳实践
    django.core.exceptions.ImproperlyConfigured: Requested setting INSTALLED_APPS
    Error: Invalid CSS after
    win32-x64-64inding.node is not a valid Win32 application
    Error: Module did not self-register
    数据分析|如何利用BI工具,探索各商品的潜在关联价值
    Python爬虫入门教程 42-100 爬取儿歌多多APP数据-手机APP爬虫部分
    Retrofit的文件上传和进度提示
    Retrofit的文件上传和进度提示
  • 原文地址:https://www.cnblogs.com/midworld/p/10471596.html
Copyright © 2011-2022 走看看