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

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

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

__iter__(self)

但是,在查看 Python 参考手册中到 迭代器类型的链接之后,并没有给出如何实现自己的示例。

有人可以发布一个代码片段(或链接到资源) ,说明如何做到这一点?

我正在编写的容器是一个 map (即按唯一键存储值)。 Dicts 可以这样迭代:

for k, v in mydict.items()

在这种情况下,我需要能够在迭代器中返回两个元素(元组?)。 目前还不清楚如何实现这样一个迭代器(尽管已经友好地提供了几个答案)。有没有人能解释一下如何实现一个类地图容器对象的迭代器?(也就是说,一个像 dict 一样的自定义类) ?

156256 次浏览

我通常会使用生成器函数。每次使用屈服语句时,它都会向序列添加一个项。

下面将创建一个迭代器,生成5个,然后生成 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

如果已经定义了 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

如果您的对象包含一组数据,您希望将您的对象的 iter 绑定到这组数据,那么您可以作弊并执行以下操作:

>>> 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

另一种选择是从合适的抽象基类继承“集合”模块,如文档 给你所示。

如果容器是它自己的迭代器,您可以从 那么您只需要实现 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 让我注意到需要澄清迭代器和可迭代容器(而不是迭代器)之间的区别。

要回答关于 地图的问题: 您提供的 __iter__应该遍历映射的 钥匙。下面是一个简单的示例,它创建了一个映射 x -> x * x,并在 Python 3上扩展了 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

在某些情况下,一个可能有效的选项是从 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

如果你不想像其他人建议的那样继承 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

它使用一个生成器,给你对此进行了很好的描述。

因为我们继承自 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)

例如,对于从 dict 继承,修改其 iter,例如,在 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

Python 中的“迭代接口”由两个方法 __next__()__iter__()组成。__next__函数是最重要的,因为它定义了迭代器行为——也就是说,函数决定了接下来应该返回什么值。使用 __iter__()方法重置迭代的起始点。通常,你会发现当 __init__()被用来设置起始点时,__iter__()只能返回 self。

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