迭代器
到目前为止,我们可能已经注意到,大多数容器对象都可以使用 for 语句进行遍历:
for element in [1, 2, 3]:
print(element)
for element in (1, 2, 3):
print(element)
for key in {'one':1, 'two':2}:
print(key)
for char in "123":
print(char)
for line in open("demo.txt", encoding='utf-8'):
print(line, end='')
复制
运行结果:
1
2
3
1
2
3
one
two
1
2
3
本文件只有这2行内容,1!
本文件只有这2行内容,2!
复制
这种访问方式清晰、简洁、方便。迭代器的使用渗透了整个 Python。在后台,for 语句对容器对象调用 iter()。该函数返回一个迭代器对象,该对象定义了 __next__() 方法,该方法每次访问容器中的一个元素。当不再有元素时,__next__() 将引发一个 StopIteration 异常,该异常告诉 for 循环终止。可以使用 next() 内置函数调用 __next__() 方法,下例显示了它的工作原理:
s = 'abc'
it = iter(s)
print(it)
print(next(it))
print(next(it))
print(next(it))
print(next(it))
复制
运行结果:
<str_iterator object at 0x000001E0C8C6F6D0>
a
b
c
Traceback (most recent call last):
File "******", line 7, in <module>
print(next(it))
StopIteration
复制
在了解了迭代器协议背后的机制后,将迭代器行为添加到类中是很容易的。定义一个 __iter__() 方法,它返回一个带有 __next__() 方法的对象。如果类定义了 __next__(),则 __iter__() 可以直接返回 self:
class Reverse:
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
rev = Reverse('一二三四五')
print(iter(rev))
for char in rev:
print(char)
复制
运行结果:
<__main__.Reverse object at 0x000001C01642F160>
五
四
三
二
一
复制
生成器
生成器是创建迭代器的简单而强大的工具。它们像普通函数一样编写,但在需要返回数据时使用 yield 语句。每次调用 next() 时,生成器都会从停止的地方恢复(它记住所有的数据值以及最后执行的语句)。下面的例子说明了生成器很容易创建:
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
for char in reverse('再向虎山行'):
print(char)
复制
运行结果:
行
山
虎
向
再
复制
如前所述,任何可以用生成器完成的事情也可以用基于类的迭代器完成。生成器之所以如此紧凑,是因为 __iter__() 和 __next__() 方法是自动创建的。
另一个关键特性是在调用之间自动保存本地变量和执行状态。这使得函数更容易编写,也比使用 self.index 和 self.data 这样的实例变量的方法更清晰。
除了自动创建方法和保存程序状态外,当生成器终止时,还会自动引发 StopIteration。结合起来,这些特性使得创建迭代器非常容易。
生成器表达式
一些简单的生成器可以被简洁地编码为表达式,使用类似于列表推导式的语法,但使用圆括号而不是方括号。这些表达式是为生成器被一个封闭函数直接使用的情况设计的。生成器表达式比完整的生成器定义更紧凑,但不够通用,并且比等价的列表推导式更容易记忆。
例子:
sumresult = sum(i*i for i in range(10))
print(sumresult)
xvec = [10, 20, 30]
yvec = [7, 5, 3]
sumproduct = sum(x*y for x,y in zip(xvec, yvec))
print(sumproduct)
data = '再向虎山行'
listresult = list(data[i] for i in range(len(data)-1, -1, -1))
print(listresult)
复制
运行结果:
285
260
['行', '山', '虎', '向', '再']
复制
官方文档:
https://docs.python.org/3.9/tutorial/classes.html