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

时间:2010-10-26 00:59:23

标签: python

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

根据this page,我需要在我的对象上实现此方法:

__iter__(self)

但是,在跟进Python参考手册中Iterator Types的链接后,没有给出如何实现自己的链接的示例。

有人可以发布一个代码段(或指向资源的链接),它会显示如何执行此操作吗?

我正在写的容器是一张地图(即通过唯一键存储值)。 dicts可以这样迭代:

for k, v in mydict.items()

在这种情况下,我需要能够在迭代器中返回两个元素(一个元组?)。 目前尚不清楚如何实现这样的迭代器(尽管已经提供了几个答案)。有人可以更多地了解如何为类似地图的容器对象实现迭代器吗? (即一个像dict一样的自定义类)?

9 个答案:

答案 0 :(得分:92)

我通常会使用生成器功能。每次使用yield语句时,它都会在序列中添加一个项目。

以下将创建一个迭代器,它返回some_list中的五个,然后返回每个项目。

def __iter__(self):
   yield 5
   yield from some_list

3.3之前,yield from不存在,所以你必须这样做:

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

答案 1 :(得分:24)

另一种选择是从`collections模块中继承适当的抽象基类,如文档here所示。

如果容器是它自己的迭代器,你可以继承 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

NB :感谢Glenn Maynard引起我的注意,一方面需要澄清迭代器与另一方面迭代器而不是迭代器之间的区别。

答案 2 :(得分:12)

如果你已经定义了next()方法(生成器对象),

通常__iter__()只返回self:

这是一个生成器的虚拟示例:

class Test(object):

    def __init__(self, data):
       self.data = data

    def next(self):
        if not self.data:
           raise StopIteration
        return self.data.pop()

    def __iter__(self):
        return self

__iter__()也可以像这样使用:     http://mail.python.org/pipermail/tutor/2006-January/044455.html

答案 3 :(得分:10)

如果你的对象包含一组你希望绑定你的对象的数据,你可以作弊并这样做:

>>> class foo:
    def __init__(self, *params):
           self.data = params
    def __iter__(self):
        if hasattr(self.data[0], "__iter__"):
            return self.data[0].__iter__()
        return self.data.__iter__()
>>> d=foo(6,7,3,8, "ads", 6)
>>> for i in d:
    print i
6
7
3
8
ads
6

答案 4 :(得分:6)

"可迭代界面"在python中包含两个方法__next__()__iter__()__next__函数是最重要的,因为它定义了迭代器行为 - 也就是说,函数确定接下来应返回的值。 __iter__()方法用于重置迭代的起始点。通常,当__iter__()用于设置起点时,您会发现__init__()可以返回自我。

请参阅以下代码以定义实现"可迭代接口的类反向"并在任何序列类的任何实例上定义迭代器。 __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

答案 5 :(得分:5)

要回答有关映射的问题:您提供的__iter__应该迭代映射的。以下是一个创建映射x -> x * x的简单示例,可以在Python3上扩展ABC映射。

import collections.abc

class MyMap(collections.abc.Mapping):
    def __init__(self, n):
        self.n = n

    def __getitem__(self, key): # given a key, return it's value
        if 0 <= key < self.n:
            return key * key
        else:
            raise KeyError('Invalid key')

    def __iter__(self): # iterate over all keys
        for x in range(self.n):
            yield x

    def __len__(self):
        return self.n

m = MyMap(5)
for k, v in m.items():
    print(k, '->', v)
# 0 -> 0
# 1 -> 1
# 2 -> 4
# 3 -> 9
# 4 -> 16

答案 6 :(得分:4)

如果您不想像其他人所建议的那样继承dict,请直接回答有关如何为自定义字典的粗略示例实现__iter__的问题:

class Attribute:
    def __init__(self, key, value):
        self.key = key
        self.value = value

class Node(collections.Mapping):
    def __init__(self):
        self.type  = ""
        self.attrs = [] # List of Attributes

    def __iter__(self):
        for attr in self.attrs:
            yield attr.key

使用生成器,这是一个很好的描述here

由于我们要继承Mapping,您还需要实施__getitem____len__

    def __getitem__(self, key):
        for attr in self.attrs:
            if key == attr.key:
                return attr.value
        raise KeyError

    def __len__(self):
        return len(self.attrs)

答案 7 :(得分:1)

可能适用于某些情况的一个选项是从dict制作自定义类继承。如果它像一个字典,它似乎是一个合乎逻辑的选择;也许它应该一个字典。这样,你就可以免费获得类似dict的迭代。

class MyDict(dict):
    def __init__(self, custom_attribute):
        self.bar = custom_attribute

mydict = MyDict('Some name')
mydict['a'] = 1
mydict['b'] = 2

print mydict.bar
for k, v in mydict.items():
    print k, '=>', v

输出:

Some name
a => 1
b => 2

答案 8 :(得分:1)

来自dict的inhert示例,修改其iter,例如,在for for循环中跳过键2

# method 1
class Dict(dict):
    def __iter__(self):
        keys = self.keys()
        for i in keys:
            if i == 2:
                continue
            yield i

# method 2
class Dict(dict):
    def __iter__(self):
        for i in super(Dict, self).__iter__():
            if i == 2:
                continue
            yield i