python生成器

Posted on   foghorn  阅读(132)  评论(0编辑  收藏  举报

什么是生成器

通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator

生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。

生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但是,不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器

如何创建生成器

方法一

将列表推导式中的[]换成()即可得到一个生成器。

g = (i for i in range(10))

print(g)

>>> <generator object <genexpr> at 0x0000022C5A64B138>

g是一个生成器,可以产生数字0~9。

方法二

在一个普通函数中使用 yield 关键字。

引例

实现一个函数,返回字符串里每个单词的首字母所对应的下标

方法一

用一个列表保存字符串里每个单词的首字母所对应的下标,然后返回这个列表即可

def index_words(text):
    result = []
    if text:
        result.append(0)
        for idx, letter in enumerate(text):
            if letter == ' ':
                result.append(idx + 1)
    return result

address = 'Four score and seven years ago...'
re = index_words(address)
输出: [0, 5, 11, 15, 21, 27]

方法二

使用生成器函数

def index_word_iter(text):
    if text:
        yield 0
    for idx, letter in enumerate(text):
        if letter == ' ':
            yield idx + 1

address = 'Four score and seven years ago...'
re = list(index_word_iter(address))
输出: [0, 5, 11, 15, 21, 27]

生成器的工作原理

(1)生成器(generator)能够迭代的关键是它有一个next()方法,

  工作原理就是通过重复调用next()方法,直到捕获一个异常。

(2)带有 yield 的函数不再是一个普通函数,而是一个生成器generator。

  可用next()调用生成器对象来取值。next 两种方式 t.__next__() | next(t)。

  可用for 循环获取返回值(每执行一次,取生成器里面一个值)

  (基本上不会用next()来获取下一个返回值,而是直接使用for循环来迭代)。

(3)yield相当于 return 返回一个值,并且记住这个返回的位置,下次迭代时,代码从yield的下一条语句开始执行。

(4)send() 和next()一样,都能让生成器继续往下走一步(下次遇到yield停),但send()能传一个值,这个值作为yield表达式整体的结果

  ——换句话说,就是send可以强行修改上一个yield表达式值。比如函数中有一个yield赋值,a = yield 5,第一次迭代到这里会返回5,a还没有赋值。第二次迭代时,使用.send(10),那么,就是强行修改yield 5表达式的值为10,本来是5的,那么a=10。

send()使用实例

用生成器实现一个斐波那契数列,先不使用send(),观察程序的输出结果。

"""
用生成器实现斐波那契数列
"""
import sys

def fibonacci(n):
    first, second, counter = 0, 1, 0

    while True:
        if counter >= n:
            return
        yield first
        first, second = second, first + second
        counter += 1


if __name__ == "__main__":
    fb = fibonacci(5)
    while True:
        try:
            print(next(fb))
        except StopIteration:
            sys.exit()

程序输出如下:
0
1
1
2
3

输出解释:程序的功能是输出斐波那契数列的前5个元素。从主函数开始执行,遇到第一个print(next(fb))语句时,会进入fionacci()函数中,并且执行到yield first语句便停止,将first返回,此时first的值是0。(注意yield first语句后面的程序不会被执行)。当主函数执行到下一个print(next(fb))语句时,fionacci()函数将从上次暂停的位置继续往下执行,执行到下一个yield first语句再次停止。

我们使用send()语句使yield表达式有一个返回结果,代码修改为:

import sys

def fibonacci(n):
    first, second, counter = 0, 1, 0

    while True:
        if counter >= n:
            return
        a = yield first
        print('counter: ', counter, 'a: ', a)
        first, second = second, first + second
        counter += 1


if __name__ == "__main__":
    fb = fibonacci(5)
    while True:
        try:
            print(next(fb))
            fb.send(10)
        except StopIteration:
            sys.exit()

输出:
0
counter:  0 a:  10
counter:  1 a:  None
1
counter:  2 a:  10
counter:  3 a:  None
3
counter:  4 a:  10

程序只输出了数列中的3个元素,并不是5个,但不是因为程序执行发生了错误,注意send()的功能,send() 和next()一样,都能让生成器继续往下走一步(下次遇到yield停)

相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具

随笔 - 73, 文章 - 0, 评论 - 4, 阅读 - 45611

Copyright © 2025 foghorn
Powered by .NET 9.0 on Kubernetes

点击右上角即可分享
微信分享提示