迭代器协议¶
在 Python 中创建一个迭代器(或者说一个惰性序列)的最简单的方法是定义一个生成器函数,如”Callables”
一章所讨论的那样。只需在函数体内需要生成值的位置(通常在循环中)使用 yield
语句即可。
或者最简单的方法是使用 builtin 或标准库提供许多可迭代对象,而不是自己从头编写一个对象。生成器函数 是定义一个返回迭代器的函数的语法糖。
许多对象含有用来返回迭代器的 .__iter__()
方法,这个方法通常被内建函数 iter()
或者循环这个
对象(e.g., for item in collection: ...)时调用。
迭代器是通过调用 iter(something)
所产生的对象,它自身也具有 .__iter__()
方法,这个
方法会返回它本身。迭代器具有的另一个方法是 .__next__()
。迭代器自身仍具有 .__iter__()
方法的原因是为了让 iter()
幂等。That is, this identity should always hold
(or raise TypeError(“object is not iterable”) ):
iter_seq = iter(sequence)
iter(iter_seq) == iter_seq
上面讲的有点抽象,所以我们来看几个具体的例子:
>>> lazy = open('06-laziness.md') # iterate over lines of file
>>> '__iter__' in dir(lazy) and '__next__' in dir(lazy)
True
>>> plus1 = map(lambda x: x+1, range(10))
>>> plus1 # iterate over deferred computations
<map at 0x103b002b0>
>>> '__iter__' in dir(plus1) and '__next__' in dir(plus1)
True
>>> def to10():
... for i in range(10):
... yield i
...
>>> '__iter__' in dir(to10)
False
>>> '__iter__' in dir(to10()) and '__next__' in dir(to10())
True
>>> l = [1,2,3]
>>> '__iter__' in dir(l)
True
>>> '__next__' in dir(l)
False
>>> li = iter(l) # iterate over concrete collection
>>> li
<list_iterator at 0x103b11278>
>>> li == iter(li)
True
在函数式编程中,或者为了追求可读性,使用生成器函数来编写自定义迭代器是再自然不过的事了。不过,我们还 可以创建遵循协议的类;通常它们也具有其他的行为(i.e., methods),但大多数此类行为依赖于有状态和副作用。 例如:
from collections.abc import Iterable
class Fibonacci(Iterable):
def __init__(self):
self.a, self.b = 0, 1
self.total = 0
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
self.total += self.a
return self.a
def running_sum(self):
return self.total
# >>> fib = Fibonacci()
# >>> fib.running_sum()
# 0
# >>> for _, i in zip(range(10), fib):
# ... print(i, end=" ")
# ...
# 1 1 2 3 5 8 13 21 34 55
# >>> fib.running_sum()
# 143
# >>> next(fib)
# 89
这个例子不值一提,但它展示了一个实现迭代器协议,并且还提供了额外的方法来返回实例状态的类。由于使用状态 是面向对象程序员经常做的,在函数式编程风格中,我们通常会避免这样的类。