Как реализовать __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