如何为容器对象实现__iter__(self)(Python)

162 投票
9 回答
176083 浏览
提问于 2025-04-16 06:05

我写了一个自定义的容器对象。

根据这个页面,我需要在我的对象上实现这个方法:

__iter__(self)

但是,当我跟进链接到迭代器类型的Python参考手册时,里面没有给出如何实现自己的迭代器的例子。

有没有人能发一段代码(或者链接到一个资源),展示一下怎么做?

我正在写的容器是一个映射(也就是通过唯一的键来存储值)。字典可以这样迭代:

for k, v in mydict.items()

在这种情况下,我需要能够在迭代器中返回两个元素(一个元组?)。尽管已经有好心人提供了几个答案,但我仍然不清楚如何实现这样的迭代器。能不能请大家再多解释一下,如何为一个类似于映射的容器对象实现一个迭代器?(也就是一个像字典一样工作的自定义类)

9 个回答

22

在Python中,“可迭代接口”由两个方法组成:__next__()__iter__()。其中,__next__函数是最重要的,因为它定义了迭代器的行为,也就是说,这个函数决定了下一个返回的值是什么。__iter__()方法则用于重置迭代的起始点。通常情况下,你会发现,当使用__init__()来设置起始点时,__iter__()可以直接返回自身(self)。

下面的代码展示了如何定义一个名为Reverse的类,这个类实现了“可迭代接口”,并为任何序列类的实例定义了一个迭代器。__next__()方法从序列的末尾开始,并以相反的顺序返回值。需要注意的是,实现“序列接口”的类的实例必须定义__len__()__getitem__()方法。

class Reverse:
    """Iterator for looping over a sequence backwards."""
    def __init__(self, seq):
        self.data = seq
        self.index = len(seq)

    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('spam')
>>> next(rev)   # note no need to call iter()
'm'
>>> nums = Reverse(range(1,10))
>>> next(nums)
9
36

另一种选择是从`collections`模块中的合适抽象基类继承,具体可以参考这里的文档。

如果你的容器本身就是一个迭代器,你可以从collections.Iterator继承。这样你只需要实现next这个方法就可以了。

下面是一个例子:

>>> from collections import Iterator
>>> class MyContainer(Iterator):
...     def __init__(self, *data):
...         self.data = list(data)
...     def next(self):
...         if not self.data:
...             raise StopIteration
...         return self.data.pop()
...         
...     
... 
>>> c = MyContainer(1, "two", 3, 4.0)
>>> for i in c:
...     print i
...     
... 
4.0
3
two
1

在查看collections模块时,如果更合适的话,可以考虑从SequenceMapping或其他抽象基类继承。这里有一个Sequence子类的例子:

>>> from collections import Sequence
>>> class MyContainer(Sequence):
...     def __init__(self, *data):
...         self.data = list(data)
...     def __getitem__(self, index):
...         return self.data[index]
...     def __len__(self):
...         return len(self.data)
...         
...     
... 
>>> c = MyContainer(1, "two", 3, 4.0)
>>> for i in c:
...     print i
...     
... 
1
two
3
4.0

注意: 感谢Glenn Maynard提醒我需要澄清迭代器和可迭代容器之间的区别。

177

我通常会使用生成器函数。每次你使用yield语句时,它会把一个项目添加到序列中。

下面的代码会创建一个迭代器,它会先返回数字五,然后返回some_list中的每一个项目。

def __iter__(self):
    yield 5
    yield from some_list

在Python 3.3之前,yield from这个功能是不存在的,所以你必须这样做:

def __iter__(self):
    yield 5
    for x in some_list:
        yield x

撰写回答