外观
迭代器和生成器
迭代器
我们发现,for
语句后面可以跟有很多的语句:列表,元组,字典,range
函数……
他是怎么做到兼容的呢?事实上,这些都是可迭代对象,Python 中的 for
语句便是将它们视为迭代器统一处理。
Python 内置的与迭代器有关的函数是 iter
和 next
生成一个迭代器
此处我们将列表转换为迭代器:
运行代码
>>> a = [1, 2, 3]
>>> b = iter(a)
>>> b
<list_iterator object at 0x0000025072FDAB30>
>>> next(b)
1
>>> next(b)
2
>>> next(b)
3
>>> next(b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
可以看到,我们通过列表 a
生成了迭代器对象 b
,然后调用 next(b)
来不断取出 b
的下一项,直到 b
中的项全部都被使用过后,抛出 StopIteration
异常。
自定义迭代器
自己定义的类也可以作为迭代器,只要实现了 __iter__
(用于初始化)和 __next__
(用于获取下一项)方法。
运行代码
class MyClass:
def __iter__(self):
self.a = 0
return self
def __next__(self):
self.a += 1
return self.a
my_object = MyNumbers()
my_iter = iter(my_object)
print(next(my_iter)) # 输出:1
print(next(my_iter)) # 输出:2
print(next(my_iter)) # 输出:3
由前文描述我们知道了,迭代器结束的标志是抛出 StopIteration
异常,因此自定义迭代器也是如此,例如:
class MyClass:
def __iter__(self):
self.a = 0
return self
def __next__(self):
if self.a <= 5:
self.a += 1
return self.a
else:
raise StopIteration
my_object = MyClass()
my_iter = iter(my_object)
for 语句的本质
先来回忆一下 for 语句的结构:
for 循环变量 in 迭代变量:
循环体
上面的 for 语句会执行如下行为:
- 获取迭代变量的迭代器
迭代器 = iter(迭代变量)
- 每次将循环变量赋值为
循环变量 = next(迭代器)
- 执行循环体
- 重复 2 和 3,直至 2 抛出 StopIteration 异常
此处我用 while
和 try...except...
语句手撕一个 for
语句出来,仅仅为了帮大家更深入理解迭代器,实际上的 for
语句肯定比这复杂得多(复杂在哪里我也不知道……)。
假设 for
语句的代码如下:
for i in data_to_iterate:
do_something(i)
那么while
语句实现同样的功能长这样:
__temp = iter(data_to_iterate)
while True:
try:
i = next(__temp)
except StopIteration:
break
do_something(i)
生成器
即一个函数返回数据时不使用 return
,用 yield
替代之。对于使用yield
的函数,Python 会先正常执行代码,遇到yield
就将其后面的值作为迭代器的下一项交给主程序使用,直到函数退出后迭代器结束。显然地,yield
与return
不同,可以多次返回。
你也可以理解为,生成器函数返回了一个可迭代对象,只不过是分批返回的,这样子更加节省内存空间,对于大量数据可以有效提高程序效率。
运行代码
def my_gen():
for i in range(3):
print('in:function', i)
yield i
for i in my_gen():
print('in:for', i)
输出:
in:function 0
in:for 0
in:function 1
in:for 1
in:function 2
in:for 2