01. Python迭代器、生成器
本篇根据菜鸟教程-Python3 迭代器与生成器改写
0.1.1. 迭代器
迭代是Python最强大的功能之一,是访问集合元素的一种方式。迭代器具有以下几种特点:
- 是一个可以记住遍历的位置的对象。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束,所有元素访问结束后抛出
StopIteration
异常。 - 只能往前不会后退。
- 迭代器有两个基本的方法:iter() (用于创建迭代器对象)和 next() (遍历下一个迭代器对象)。
- 字符串,列表或元组对象都可用于创建迭代器;
0.1.1.1. 迭代器创建以及遍历
- 使用iter()方法创建迭代器对象
list = [1, 2, 3, 4] # 创建列表
it = iter(list) # 创建迭代器对象
print (next(it)) # 输出迭代器的下一个元素
---->
1
print (next(it))
---->
2
- 使用for语句正常进行迭代器遍历:
list = [1,2,3,4]
it = iter(list) # 创建迭代器对象
for x in it:
print (x, end=" ")
---->
1 2 3 4
- 使用next方法进行迭代遍历
import sys # 引入 sys 模块
list = [1,2,3,4]
it = iter(list) # 创建迭代器对象
while True:
try:
print (next(it))
except StopIteration:
sys.exit()
---->
1
2
3
4
0.1.1.1.1. 创建迭代器
创建迭代器对象时需要在类中实现两个方法:
__iter__()
方法:实现了__next__()
方法并通过StopIteration
异常标识迭代完成;__next__()
方法:会返回下一个迭代器对象;
- 例:创建数值迭代器,初值为1,逐步增1:
class MyNumbers:
def __iter__(self):
self.a = 1 # 类中的 self.a 可随便命名,可以为self.bb等等,只是一个普通的成员属性名,但是上下要一致
return self
def __next__(self):
if self.a <= 5:
x = self.a
self.a += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
print(x)
---->
1
2
3
4
5
0.1.2. 生成器
Python 中,使用了 yield
的函数被称为生成器(generator)。生成器是一个返回迭代器的函数,只能用于迭代操作,可简单理解为生成器就是一个迭代器。调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield
的值, 并在下一次执行 next()
方法时从当前位置继续运行。调用一个生成器函数,返回的是一个迭代器对象。
- 例:使用 yield 实现斐波那契数列:
import sys
def fibonacci(n): # 生成器函数 - 斐波那契
a, b, counter = 0, 1, 0
while True:
if (counter > n):
return
yield a
a, b = b, a + b
counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
while True:
try:
print (next(f), end=" ")
except StopIteration:
sys.exit()
0.1.3. 生成器与迭代器的异同
对于生成器,Python会自动实现迭代器协议(迭代器协议是指对象需要提供next方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代; 可迭代对象就是:实现了迭代器协议的对象),以便应用到迭代背景中。由于生成器自动实现了迭代器协议,所以,我们可以调用它的next方法,并且,在没有值可以返回的时候,生成器自动产生StopIteration异常。
状态挂起:生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行。
对于这一点,当建立生成器,使用若干次next
操作后会记住当前的位置,之后继续采用for循环的时候会继续以此断点为起点继续进行,但是迭代器使用若干次next
操作后,继续使用for循环会再次从头开始,而不会从断点继续开始。
- 生成器会继续断点之后的操作,直至耗尽生成器
class MyNumbers:
def __iter__(self):
self.bb = 1
return self
def __next__(self):
if self.bb <= 5:
x = self.bb
self.bb += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
gen = enumerate(myiter) # enumerate会自动将迭代器变为生成器
print(next(gen))
---->
(0, 1)
for i, b in gen: # 从断点开始
print(b)
---->
2
3
4
5
- 迭代器在之前要是经历过迭代操作,使用for循环进行迭代会再次从头开始迭代,而不是从断点开始;
class MyNumbers:
def __iter__(self):
self.bb = 1
return self
def __next__(self):
if self.bb <= 5:
x = self.bb
self.bb += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
print(next(myiter))
---->
1
for i in myiter:
print(i)
---->
1
2
3
4
5