《老鸟python 系列》视频上线了,全网稀缺资源,涵盖python人工智能教程,爬虫教程,web教程,数据分析教程以及界面库和服务器教程,以及各个方向的主流实用项目,手把手带你从零开始进阶高手之路!点击 链接 查看详情




生成器

阅读:227568410    分享到

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

如果我们可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的 list,从而节省大量的空间,在 Python 中,这种一边循环一边计算的机制,称为生成器(Generator)。

生成器定义和使用

要创建一个生成器,有很多种方法,最简单的方式是:只要把一个列表推导式的 [] 改成 (),就创建了一个生成器。

myge = (item for item in range(10))
print(type(myge))

我们可以直接输出集合的每一个元素,但我们怎么输出生成器的每一个元素呢, 如果要一个一个输出元素,可以通过生成器的 __next__ 函数或者 Python 内置的 next 函数(实际上该函数间接调用生成器的 __next__ 函数)。

myge = (item for item in range(3))
print(myge.__next__())
print(myge.__next__())
print(myge.__next__())

# 等价于上面代码
myge = (item for item in range(3))
print(next(myge))
print(next(myge))
print(next(myge))

生成器保存的是算法,每次调用 __next__ 函数 就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出 StopIteration 的错误。

myge = (item for item in range(3))
print(myge.__next__())
print(myge.__next__())
print(myge.__next__())
print(myge.__next__())  # 抛出异常

用生成器的 __next__ 方法太单一不灵活,我们可以使用 for 循环,因为生成器也是可迭代对象。

from collections.abc import Iterable

myge = (item for item in range(3))
print(isinstance(myge, Iterable))

for item in myge:
    print(item)

注意事项:我们基本上永远不会调用 next 函数,而是通过 for 循环来迭代它,因为 for 循环在遍历生成器时, 计算到最后一个元素就会自动停止遍历,不会抛出 StopIteration 的错误,这样很友好。

让函数变成生成器

生成器非常强大,如果推算的算法比较复杂,用类似列表推导式的 for 循环无法实现的时候,还可以用函数来实现, 我们先来看一个简单的例子,写一个可以生成无限偶数的生成器。

def ge_even():
    data = 1
    while True:
        if data % 2 == 0:
            yield data
        data += 1

even = ge_even()
print(type(even))  # even 是个生成器

for item in even:
    print(item)

只要函数定义中包含 yield 关键字,那么这个函数就不再是一个普通函数,而是一个生成器,该生成器和其它生成器具有一样的性质, 比如可以用他的 __next__ 函数,可以用 for 循环遍历。

def ge_func():
    yield 1
    yield 2
    yield 3

myge = ge_func()
print(myge.__next__())
print(myge.__next__())
print(myge.__next__())

myge = ge_func()
for item in myge:
    print(item)

本节重要知识点

生成器和推导式的区别。

遍历生成器的方法。

如何把函数变成生成器。

作业

著名公司(某东)笔试题:运行一下看看结果,想想为什么第一种调用和后两种调用的结果不一致。

def ge_func():
    yield 1
    yield 2
    yield 3

# 第一种调用
print(ge_func().__next__())
print(ge_func().__next__())
print(ge_func().__next__())

# 第二种调用
myge = ge_func()
for item in myge:
    print(item)

# 第三种调用
for item in ge_func():
    print(item)

如果以上内容对您有帮助,请老板用微信扫一下赞赏码,赞赏后加微信号 birdpython 领取免费视频。


登录后评论

user_image
逗妇乳
2024年2月3日 11:47 回复

作业:因为第一种调用的方式是多次调用生成器函数,每一次的调用都会创建一个新的生成器对象,每一次都是在一个新的生成器上调用__next__(),所以他们之间的状态是独立的,因此每个结果都是1,可以写为这样,结果就是一样的了myge = ge_func() print(myge.next()) print(myge.next()) print(myge.next())


user_image
RednaxelaFX
2020年8月25日 04:02 回复

深入浅出, 感谢!


user_image
一条叫小白的黑狗
2020年1月23日 15:50 回复

一直不知道生成器是什么,现在终于清晰了


user_image
夏之幻想
2019年11月2日 19:40 回复

生成器,迭代器,可迭代对象,生成器表达式。这几个概念的意思和区别。这个问题问过来面试python开发的小同学好多遍,没一个能清晰的讲出来的,我都怀疑是不是不该问概念性问题了


user_image
Sophomore
2020年9月17日 19:46

都在忙着各种业务,沉下来把基础学扎实的只是少部分


user_image
木大木大
2019年8月26日 23:45 回复

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator


user_image
慕容腹黑
2020年10月2日 18:11

list、dict、str既不是生成器,也不是迭代器,他只是可迭代的对象


user_image
Narc
2019年7月18日 19:54 回复

比一般博客清晰很多