迭代器和生成器

发布时间 2023-12-12 14:46:20作者: 小满三岁啦

迭代器

什么是迭代器

迭代器指的是迭代取值的工具,迭代是一个重复的过程,每次重复都是基于上一次的结果而继续的,单纯的重复并不是迭代。

为何要有迭代器

迭代器是用来迭代取值的工具,而涉及到把多个值循环取出来的类型有: 列表,字符串,元组,字典,集合,打开文件

文件是可迭代对象,也是迭代器

with open("关于成长.txt", "wt", encoding="utf-8") as file_obj:
    file_obj.write("你懂的越多,你就越像这个世界的孤儿。")

with open("关于成长.txt", "rt", encoding="utf-8") as file:
    file_iter = file.__iter__()  # file_iter = iter(file)
    print(file_iter.__next__())
    print(file_iter.__next__())
Traceback (most recent call last):
  File "E:\xxx.py", line 13, in <module>
    print(file_iter.__next__())
StopIteration
hero_list = ["小满", "大乔", "王昭君"]
index = 0

while index < len(hero_list):
    print(hero_list[index])
    index += 1
小满
大乔
王昭君

上述迭代取值的方式只适用于有索引的数据类型:列表、字符串、元组

为了解决基于迭代器取值的局限性 python必须提供一种能够不依赖于索引的取值方式,这就是迭代器

如何使用迭代器

什么是可迭代对象

但凡内置有__iter__方法的,都称之为可迭代对象

可迭代对象(iterable)指的是可以依次返回其内部成员的对象。比如字符串、列表、字典、文件等都是可迭代对象。从语法方面说就是那些具有__iter__的方法的对象,对于类来说指的是那些具有__getitem__方法的对象。

s1 = ''  # 字符串
s1.__iter__()
 
l = [] # 列表
l.__iter__()

t = (1,)  # 元组
t.__iter__()

d = {'name': '小满'}  # 字典
d.__iter__()

set1 = {1, 2, 3}  # 集合
set1.__iter__()

with open('迭代对象.txt', mode='w', encoding='utf-8') as f:  # 文件
    f.__iter__()
	f.__next__()

如何使用迭代器

调用可迭代对象下的__iter__方法,会将其转成迭代器对象

dic = {"a": 1, "b": 2, "c": 3}
dic_iterator = dic.__iter__()  # 相当于iter(dic)

print(dic_iterator.__next__())  # 相当于next(dic_iterator)
print(dic_iterator.__next__())
print(dic_iterator.__next__())
print(dic_iterator.__next__())
a
b
c
Traceback (most recent call last):
  File "E:\xxx.py", line 12, in <module>
    print(dic_iterator.__next__())
StopIteration

在一个迭代器取值干净的情况下,再对其取值取不到。

dic = {"a": 1, "b": 2, "c": 3}
items = dic.__iter__()

while True:
    try:
        print(items.__next__())
    except StopIteration:
        break
print("==========>")

while True:
    try:
        print(items.__next__())  # 取不到了
    except StopIteration:
        break

a
b
c
==========>

如果想再取一次怎么办?重新赋值一次

dic = {"a": 1, "b": 2, "c": 3}
items = dic.__iter__()

while True:
    try:
        print(items.__next__())
    except StopIteration:
        break
print("==========>")

items = dic.__iter__()
while True:
    try:
        print(items.__next__())  # 取不到了
    except StopIteration:
        break
a
b
c
==========>
a
b
c

可迭代对象与迭代器对象详解

可迭代对象可以转成成迭代器对象:内置有__iter__方法的对象。

可迭代对象.__iter__()得到迭代器对象

迭代器对象:内置有__iter__方法并且内置有__next__方法的对象

迭代器对象.__next__():得到迭代器的下一个值
迭代器对象.__iter__():得到迭代器本身,说白了调了跟没调一样

看案例

dic = {"a": 1, "b": 2, "c": 3}
items = dic.__iter__()

print(items is items.__iter__().__iter__().__iter__().__iter__())

True

迭代器优缺点

优点

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

惰性取值;每次只取一个数据,不占内存;迭代器保存的是产生数据的算法,而不是实际的数据

缺点

除非取尽,否则无法获取迭代器的长度;

只能往后依次取值,不能返回头往前取值。就像象棋中的卒,只进不退。

for循环的工作原理

for循环可以称之为迭代器循环

  1. dic.__iter__()得到一个迭代器对象
  2. 迭代器对象.__next__()拿到一个返回值,然后将该返回值赋值给for index in ...中的index
  3. 循环往复步骤2,直到抛出异常StopIteration异常for循环会捕捉异常然后结束循环

list("hello")原理同for循环

生成器

如何自定义生成器:

在函数内一旦存在yield关键字,调用函数并不会执行函数体代码,会返回一个生成器对象,生成器即自定义的迭代器。 如果取完了一样会报StopIteration错误

def func():
    print("第1次")
    yield 1
    print("第2次")
    yield 2
    print("第3次")
    yield 3
    print("第4次")


g = func()

# 会触发函数体代码的运行,遇到yield停下来,将yield后面的值当做本次调用后的返回结果
res = g.__next__()  # next(g)
print(res)

res2 = g.__next__()
print(res2)

res3 = g.__next__()
print(res3)

res4 = g.__next__()
print(res4)
第1次
1
第2次
2
第3次
3
第4次
报错 StopIteration

应用案例

def my_range(start, stop, step=1):
    print("start...")

    while start < stop:
        yield start
        start += step
    print("end...")


res = my_range(1, 5, 2)
print(next(res))  # res.__next__()
print(next(res))  # res.__next__()
print(next(res))  # res.__next__()

start...
1
3
end...
报错:StopIteration
def my_range(start, stop, step=1):
    print("start...")

    while start < stop:
        yield start
        start += step
    print("end...")


for index in my_range(1, 9, 2):
    print(index)
start...
1
3
5
7
end...

yield的表达形式

def hero(name):
    print(f"{name}准备吃东西啦...")
    while True:
        # food拿到的就是yield接收的值
        food = yield
        print(f"{name}吃了{food}")


g = hero("小满")
g.send(None)  # 等同于 next(g)
g.send("红buff")
g.send("蓝buff")
g.close()  # 关闭后无法传值

g.send("红buff")
小满准备吃东西啦...
小满吃了红buff
小满吃了蓝buff
报错:StopIteration