当前位置:首页>python>【21】Python生成器

【21】Python生成器

  • 2026-10-21 13:29:15
【21】Python生成器
一、迭代器

迭代器就是迭代取值的工具,每一次取值都可以保存上一次取值的最终状态。

迭代器的核心能力是通过 next() 方法逐个取出元素,这是遍历可迭代对象的底层逻辑。

可迭代对象

可迭代对象就是具有 __iter__ 方法的对象。

#字符串name = "dream"# <str_iterator object at 0x000001D2B239B220>print(name.__iter__()) #列表# <list_iterator object at 0x000001D2B239B220>print([1, 2, 3].__iter__())#元组# <tuple_iterator object at 0x000001D2B239B220>print((1, 2, 3).__iter__())#字典# <dict_keyiterator object at 0x000001D2B232A2F0>print({"name": "dream", "age": 18}.__iter__())#集合# <set_iterator object at 0x000001D2B236A840>print({1, 2, 3}.__iter__())

字符串、列表、元组、集合和字典既是可迭代对象又是迭代器对象。

迭代器基于索引的迭代取值,所有迭代的状态都保存在了索引中,而基于迭代器实现迭代的方式不再需要索引,所有迭代的状态就保存在迭代器中,然而这种处理方式优点与缺点并存。

优点

● 为序列和非序列类型提供了一种统一的迭代取值方式。

● 惰性计算:迭代器对象表示的是一个数据流,可以只在需要时才去调用next来计算出一个值。就迭代器本身来说,同一时刻在内存中只有一个值,因而可以存放无限大的数据流,而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内存大小的限制,可以存放的值的个数是有限的。

缺点

除非取尽,否则无法获取迭代器的长度,只能取下一个值,不能回到开始,更像是‘一次性的’,迭代器产生后的唯一目标就是重复执行next方法直到值取尽,否则就会停留在某个位置,等待下一次调用next。若是要再次迭代同个对象,你只能重新调用iter方法去创建一个新的迭代器对象,如果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值。

name_iter = "dream".__iter__() #将字符串 "dream" 转换为迭代器对象print(name_iter.__next__()) # dprint(name_iter.__next__()) # rprint(name_iter.__next__()) # eprint(name_iter.__next__()) # aprint(name_iter.__next__()) # mname_iter = "dream".__iter__() print(name_iter.__next__()) # d

二、生成器

Python中的生成器是一种特殊的迭代器,可以在需要时生成数据,而不必提前从内存中生成并存储整个数据集。

通过生成器,可以逐个生成序列中的元素,而无需一次性生成整个序列。生成器在处理大数据集时,具有节省内存、提高效率的特点。

# 元组生成式g = (x * 2 for x in range(3))print(g) # <generator object <genexpr> at 0x1291d1e70>print(g.__next__()) # 0print(g.__next__()) # 2print(g.__next__()) # 4print(g.__next__()) # 报错,输出:StopIteration

遍历循环取值的时候如果到了最后一个元素不会报错的,在for 循环内部帮助我们处理了 StopIteration 这个错误。

关键字 yield可以暂时保存当前的状态并且卡住。

def my_generator():    yield 1    yield 2    yield 3# <generator object my_generator at 0x104307840>print(my_generator()) iter = my_generator()print(iter.__next__()) #1 print(iter.__next__()) #2print(iter.__next__()) #3

yield练习:

def eater():    print(f"当前开始吃饭")    while True:        # 通过eat_iter.__next__() 启动生成器 走到 yield 卡住        food = yield        # eat_iter.send("鱼香肉丝")        print(f"开始吃{food}")  # 开始吃鱼香肉丝# <generator object eater at 0x1042a7840>print(eater())# 当前开始吃饭# Noneprint(eater().__next__())# 向生成器的 yield 中传值# 先调用函数获取得到一个生成器对象,这个生成器对象还没有启动eat_iter = eater()# 手动启动生成器eat_iter.__next__()# 向 yield 传值eat_iter.send("鱼香肉丝")  # 开始吃鱼香肉丝eat_iter.send("宫保鸡丁")  # 开始吃宫保鸡丁

使用装饰器帮助我们完成创建对象和启动:

def init(func):  # func 接收被装饰的函数(这里是eater生成器函数)    def inner(*args, **kwargs):  # 接收被装饰函数的参数(这里eater无参数)        # 1. 调用被装饰的函数,得到生成器对象        g = func(*args, **kwargs)  # func就是eater,调用eater()生成生成器对象g        # 2. 手动预激生成器:让生成器执行到第一个yield处暂停        next(g)  # 等价于g.__next__(),启动生成器对象        # 3. 返回预激后的生成器对象        return g    return inner  # 装饰器返回内部函数

eater是一个无限循环的生成器函数(因为while True),核心逻辑是接收外部发送的 “食物名称”,并打印 “吃该食物”。

@init  # 用init装饰器装饰eater,调用eater()时实际执行init(eater)()def eater():    print(f"当前开始吃饭")  # 生成器被预激时执行这行    while True:  # 无限循环,持续接收食物        # 关键点:yield在这里是“接收数据”的作用,执行到这里会暂停        food = yield  # 等待外部send()发送数据,数据会赋值给food        print(f"开始吃{food}")  # 收到数据后执行这行

第一步:执行 eat_iter = eater()

因为eater被@init装饰,所以调用eater()实际是执行init(eater),返回的inner函数。

①、执行g = func(*args, **kwargs):func是eater,调用eater()生成一个未激活的生成器对象g。

②、执行next(g):启动生成器,触发eater函数执行:先打印当前开始吃饭;执行到food = yield处暂停(生成器等待接收数据);

③、inner函数返回预激后的生成器对象g,赋值给eat_iter。

第二步:执行 eat_iter.send("鱼香肉丝")

send()方法的作用是:向生成器发送数据,并让生成器从暂停的yield处继续执行:

①、把"鱼香肉丝"赋值给food(即food = "鱼香肉丝");

②、执行print(f"开始吃{food}") :打印开始吃鱼香肉丝;

③、再次进入while True循环,执行到food = yield处,再次暂停,等待下一次发送数据。

最新文章

随机文章