迭代器和生成器
迭代器
我们发现,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)生成器 yield
yield 是一个关键字,用于定义生成器函数,生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。
简单来说,生成器函数返回了一个可迭代对象。这样做的优势是更加节省内存空间,对于大量数据可以有效提高程序效率。
运行代码
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特别说明的是,在初次调用生成器函数时,其中的代码不会被执行。你可以尝试运行下面的例子来具体了解:
运行代码
def a():
print(1)
yield 'SOME VALUE'
x = a()
print(2)
next(x)
print('done')