什么是Python迭代器

244 阅读2分钟

在python中,我们可以使用下面的for-in循环迭代一个列表

numbers = [1, 2, 3]

for n in numbers:

    print(n)

但是for-in循环的原理是怎样的呢?这里就涉及到了迭代器相关的部分

迭代器协议

Python的迭代器协议要求实现__iter__方法,__iter__方法会返回一个迭代器对象,这个迭代器对象实现了__next__方法,并且通过抛出StopIteration异常标识迭代的完成。

根据迭代器协议,一个可能的迭代器实现是这样的:

class InfiniteProducer(object):

    def __next__(self):

        return "hello world"


class MyIterator(object):

    def __iter__(self):

        return InfiniteProducer()


for i in MyIterator():

    print(i)

在这里,我们用两个类实现了迭代器协议,在MyIterator类中,我们在__iter__中返回了一个InfiniteProducer迭代器对象,在这个迭代器对象中,我们实现了__next__方法,这个__next__方法会一直产出同样的值。

在这里,我们使用InfiniteProducer这个类来实现__next__方法,然后用它从迭代器中获取值。

实际上,在迭代器协议中,__next__在哪里定义并不重要,重要的是__iter__要返回带有__next__方法的对象。

所以从实用的角度出发,我们可以将__next__方法和__iter__方法实现在同一个类里面,像下面这样:

class MyIterator(object):

    def __iter__(self):

        return self


    def __next__(self):

        return "hello world"


for i in MyIterator():

    print(i)

现在,因为MyIterator实现了__iter__方法,所以它是可迭代的,同时因为它还实现了__next__方法,所以它又是一个迭代器。

因为这个类实现了__next__方法,所以在__iter__方法中返回实例自身,就满足了迭代器协议。

停止迭代器

上面的迭代器会一直返回值,如何停止迭代器呢?

根据迭代器协议,我们需要抛出StopIteration异常来终止一个迭代器

比如,如果我们要在迭代10次以后终止,我们可以这么写

class LimitProducer(object):

    def __init__(self):

        self.iter_nums = 0

        self.iter_total_count = 10

    def __next__(self):

        if self.iter_nums < self.iter_total_count:

            self.iter_nums += 1

            return "hello world"

        raise StopIteration


class MyIterator(object):

    def __iter__(self):

        return LimitProducer()


for i in MyIterator():

    print(i)

生成器

python的生成器提供了一种实现迭代器协议的简单方法

我们上面一直产出值的迭代器,可以通过生成器这样实现:

def iterator():

    while True:

        yield "hello world"

同样地,通过抛出StopIteration异常可以终止一个迭代器

def iterator():

    iter_total_count = 10

    iter_nums = 0

    while True:

        if iter_nums < iter_total_count:

            iter_nums += 1

            yield "hello world"

        else:

            raise StopIteration

串联迭代器

生成器可以串联起来,构建像管道一样的数据处理算法,数据处理时,每次只处理一个元素

test_l = range(10)


def square(seq):

    for i in seq:

        yield i**2


def incr(seq):

    for i in seq:

        yield i + 1


test_res = incr(square(test_l))

for i in test_res:

    print(i)

现在我们来解答开头的问题:for-in 循环是怎么迭代列表的?

对一个列表进行迭代,实际上会先调用列表的__iter__方法获得迭代器,然后在这个迭代器上面调用__next__方法从中获取值

l_iterator = iter([1, 2, 3])  # 获取迭代器

print([i for i in dir(l_iterator) if i in ("__iter__", "__next__")])

>>> ['__iter__', '__next__']

print(l_iterator.__next__())  # 从迭代器获取一个值

>>> 1

总结

为了支持迭代,对象需要实现迭代器协议

可以通过类、生成器、生成器表达式实现一个迭代器

生成器是实现迭代器协议的一个简单方法