Как реализовать __iter __ (self) для объекта-контейнера (Python)

Я написал пользовательский объект контейнера.

В соответствии с эта страница, мне нужно реализовать этот метод для моего объекта:

__iter__(self)

Однако, следуя ссылке на Iterator Types в справочном руководстве Python, нет примеров того, как реализовать свои собственные.

Может ли кто-нибудь опубликовать фрагмент (или ссылку на ресурс), который показывает, как это сделать?

Контейнер, который я пишу, представляет собой карту (т.е. сохраняет значения по уникальным ключам). dicts можно повторить следующим образом:

for k, v in mydict.items()

В этом случае мне нужно иметь возможность возвращать в итераторе два элемента (кортеж?). Пока не ясно, как реализовать такой итератор (несмотря на несколько ответов, которые были любезно предоставлены). Может ли кто-то пролить некоторый свет на то, как реализовать итератор для картографического объекта-контейнера? (т.е. пользовательский класс, который действует как dict)?

Ответ 1

Обычно я использую функцию генератора. Каждый раз, когда вы используете оператор yield, он добавляет элемент в последовательность.

Далее будет создан итератор, который возвращает пять, а затем каждый элемент в some_list.

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

Ответ 2

Другой вариант - наследовать из соответствующего абстрактного базового класса из модуля `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, рассмотрите наследование от Sequence, Mapping или другого абстрактного базового класса, если это более уместно. Ниже приведен пример подкласса 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 за то, что я обратил мое внимание на необходимость уточнения разницы между итераторами, с одной стороны, и контейнерами, которые являются итерабельными, а не итераторами, с другой.

Ответ 3

обычно __iter__() просто верните self, если вы уже определили метод next() (объект-генератор):

вот пример Dummy генератора:

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

Ответ 4

Если ваш объект содержит набор данных, к которым вы хотите привязать свой объект 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

Ответ 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 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

Если вы не хотите наследовать от dict, как предложили другие, вот прямой ответ на вопрос о том, как реализовать __iter__ для грубого примера пользовательского dict:

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)

Ответ 7

"Итерируемый интерфейс" в python состоит из двух методов: next() и iter(). Функция next является наиболее важной, так как она определяет поведение итератора, то есть функция определяет, какое значение должно быть возвращено в следующий раз. Метод iter() используется для reset начальной точки итерации. Часто вы обнаружите, что iter() может просто вернуть self, когда init() используется для установки начальной точки.

См. следующий код для определения класса 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

Ответ 8

Один из вариантов, который может работать в некоторых случаях, состоит в том, чтобы сделать ваш собственный класс наследовать с 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

Ответ 9

пример для inhert from dict, измените его iter, например, пропустите клавишу 2, когда in for loop

# 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