python迭代器和生成器
1.迭代器
1.1 可迭代对象
可迭代对象指的是 实现了__iter__方法的对象,可以被for循环遍历的容器,比如一个列表,自定义的链表等。使用 iter() 方法获取它的迭代对象(可以理解为链表的指针)
1.2 迭代器
迭代器是指实现了 __iter__ 和 __next__方法对象,__iter__返回它本身,确保每个单独的节点都是可以被迭代的,满足链表可以从第n个元素开始访问的需求;__next__返回它的下一个节点,如果已经遍历完毕则抛出StopIteration异常。
# 实现了__iter__方法,是一个可迭代对象,可以理解为是一个整的链表,但此时还不能单独访问其中的元素 class Node: def __init__(self, data): self.data = data self.next: Node | None = None def __iter__(self): return NodeIter(self) #是迭代器对象,可以理解为指向链表首元素的指针,通过这个指针遍历所有的链表元素 class NodeIter: def __init__(self, node: Node = None): self.current = node def __iter__(self): return self def __next__(self): if self.current is None: raise StopIteration else: node = self.current self.current = self.current.next return node #返回当前节点的值1.3 可迭代对象和迭代器的区别
- 协议不同:可迭代对象只需实现
__iter__(),迭代器需要实现__iter__()和__next__() - 状态:迭代器有内部状态(当前位置),可迭代对象通常没有
- 重用性:可迭代对象可多次遍历(每次获取新迭代器),迭代器遍历一次后耗尽
- 包含关系:所有迭代器都是可迭代对象,但并非所有可迭代对象都是迭代器
依旧以链表举例,整个链表是一个可迭代对象,遍历这个对象的时候实际上是通过一个指向链表首元素的指针不断地移动到下一个元素来完成的,这个指针就可以理解为迭代器。
2.生成器
2.1 概念
生成器是一种特殊的迭代器,它通过 yield 关键字动态的返回值,而非一次性返回所有值,适合用来读取大文件或产生无限序列。生成器内部自动实现了 __iter__和__next__.
当一个函数内部使用了yield关键字时,那么它就是一个生成器函数,此时函数的返回值不再是return后的返回值,而是类似于函数的对象 :
<generator object fibonacci at 0x0000026E4B6D20A0>
以一个生成n个斐波那契的生成器举例:
def fibonacci(limit): """生成斐波那契数列""" a, b = 0, 1 count = 0 while count < limit: yield a a, b = b, a + b count += 1当执行下面的代码时:
fibo = fibonacci(10) print(fibo) #输出:<generator object fibonacci at 0x0000026E4B6D20A0> print(next(fibo)) #输出0 print(next(fibo)) #输出1 print(next(fibo)) #输出1 print(next(fibo)) #输出2fibo是一个生成器对象,这个对象可以生成10个斐波那契数,但不是同时生成在内存中,而是一个next()取回一个值,这个特性被称作 "惰性加载" 。执行第一个next()时,返回第一个yield后的值并且暂停函数的执行,等到第二次执行next()时从第一个field后开始继续执行,也就是说,当我在第一个 yield下面抛出异常,函数执行一次 next()并不会报错,因为抛出异常语句并没有执行,第二次执行next()才会报错,比如下面的代码:
def generator1(): print('第一个yield之前的语句块') yield 1 print('第一个yield之后的语句块') print(1 / 0) print('第二个yield之前的语句块') yield 2 print('第二个yield之后的语句块') ge = generator1() #产生了一个生成器对象 print(next(ge)) # 只执行 print(1 / 0) 之前的代码函数就被中断了,所有不会报错 print(next(ge)) # 回到第一个yield之后的代码继续执行,执行到print(1 / 0)语句,报错如果生成器函数中有return语句,return语句依旧有结束函数运行的作用,return之后的语句不可达,但仍然遵循上面的中断原则,并且return后的值是抛出 StopIteration异常的提示信息:
2.2 yield from
当我们的生成器的数据是从一个列表中取得并且每次返回一个元素时应该怎么写?
def generator(): user_list = ["zhang","li","wang","qian"] for username in user_list: yield username ge = generator() for i in ge: print(i)yield from就是简化上面的流程,提升生成器的效率,以上代码可以用下面的代码替代:
def generator(): user_list = ["zhang","li","wang","qian"] yield from user_list ge = generator() for i in ge: print(i)