Это отвечает только на ваш первый вопрос и может помочь вам с вопросом 2.
Цитата из «Свободного питона» (стр. 420):
[...]Объекты, реализующие метод __iter__
, возвращающий итератор, являются итеративными.[...]
Это означает, что вы могли бы (теоретически) сделать что-то вроде этого:
class Main:
def __init__(self):
self.items = list(range(1, 8))
self.length = len(self.items)
def __iter__(self):
return MainIterator(self)
Теперь, но как выглядит класс MainIterator
?Итератору просто нужен метод __next__
dunder, чтобы определить следующее возвращаемое значение.Реализация может выглядеть так:
class MainIterator:
def __init__(self, iterable):
self.iterable = iterable
self.index = 0
def __next__(self):
if self.index >= self.iterable.length:
raise StopIteration
self.index += 1
return self.iterable.items[self.index - 1]
В основном я делаю создание ссылки на вызываемый итерируемый объект и сохранение его в self.iterable
.Теперь каждый раз, когда вызывается метод dunder __next__
, он возвращает элемент массива, пока итератор не будет исчерпан.На это указывает повышение StopIteration
.
Вы не видите такой реализации очень часто, так как эти два класса часто объединяются в один класс.Я просто хотел продемонстрировать, что можно разделить их.Результатом является то, что @rbricheno уже опубликовал:
class Main:
def __init__(self):
self.items = list(range(1, 8))
self.length = len(self.items)
def __iter__(self):
self.index = 0
return self
def __next__(self):
if self.index >= self.length:
raise StopIteration
self.index += 1
return self.items[self.index - 1]
Разница в том, что __init__
возвращает сам экземпляр, так как сам класс теперь итеративный и итератор (помните: итератор имеет dunder __next__
)итератор имеет метод __iter__
dunder, который возвращает итератор).
Последний интересный момент - при вызове этих методов dunder.На самом деле, при использовании синтаксиса for in
это синтаксический сахар для:
a = Main()
## recreating the for in loop
itr = a.__iter__()
while True:
try:
print(itr.__next__())
except StopIteration:
break
Сначала инициализируется итератор, а __next__
возвращает значение, пока итератор не будет исчерпан.
РЕДАКТИРОВАТЬ:
Вы действительно должны прочитать мой пост снова.Не рекомендуется разделять итератор.Это просто чтобы продемонстрировать, как они работают внутри.Также, пожалуйста, не определяйте свои собственные методы.Это сломает ваш код в какое-то время.Я исправил ваш класс dict ниже, но я перебираю пару, а не ее компоненты.
class Pair:
def __init__(self, key, value):
self.key = key
self.value = value
## you need this to display your class in a meaningful way
def __repr__(self):
return f'{__class__.__name__}({self.key}, {self.value})'
class Dictionary:
def __init__(self):
self.items = []
self.length = len(self.items)
def add(self, objects):
self.items.append(objects)
self.length += 1
def __iter__(self):
self.index = 0
return self
def __next__(self):
if self.index >= self.length:
raise StopIteration
self.index += 1
return self.items[self.index - 1]
a = Dictionary()
a.add(Pair('up', 'above'))
a.add(Pair('down', 'below'))
for i in a:
print(i.key)
print(i.value)
Вывод на моем компьютере:
up
above
down
below