Я программирую объект-итератор, который возвращает свои модификации, и я хочу, чтобы исходный объект был частью процесса итерации, поэтому его необходимо вернуть перед изменением.
...но:
yield
внутри __next__
не будет работать, потому что вместо этого он возвращает другой тип (генератор) на каждой итерации.yield
внутри __iter__
, чтобы получить итератор, но тогда я не мог бы использовать __next__
, чтобы получить итерации по запросу (мой объект — машина Тьюринга, поэтому было бы неплохо контролируемую пошаговую печать ленты).Пример рабочего, но неэффективного кода:
from copy import deepcopy
class MyObject():
def __init__(self, n):
self.n = n
def __str__(self):
return str(self.n)
def __iter__(self):
return self
def __next__(self):
if self.n >= 10:
raise StopIteration
self_copy = deepcopy(self)
self.n += 1
return self_copy
for x in MyObject(n=7):
print(x)
Выход:
7
8
9
Я, вероятно, что-то упускаю здесь, но я не могу понять это.
Генератор кажется хорошей идеей. Замените ваши __iter__
и __next__
на это:
def __iter__(self):
while not self.n >= 10:
yield self
self.n += 1
(В этом конкретном случае мы могли бы, конечно, вместо этого сделать while self.n < 10:
.)
В качестве альтернативы, чтобы сделать ваш объект своим собственным итератором, как описано в наших комментариях ниже, вы можете использовать флаг:
class MyObject():
def __init__(self, n):
self.n = n
self.iterated_original = False
def __str__(self):
return str(self.n)
def __iter__(self):
return self
def __next__(self):
if not self.iterated_original:
self.iterated_original = True
else:
self.n += 1
if self.n >= 10:
raise StopIteration
return self
for x in MyObject(n=7):
print(x)
Или, поскольку iterated_original
в идеале установлен на True
после, оригинал был повторен, вот так поздно, как я могу это сделать:
def __next__(self):
if self.iterated_original:
self.n += 1
if self.n >= 10:
raise StopIteration
try:
return self
finally:
self.iterated_original = True
@WHoZ Что ты имеешь в виду? Вы можете сделать это с этим.
@WHoZ А, думаю, понял, что ты имеешь в виду. Вы могли бы сделать myiter = iter(MyObject(n=7))
для этого. Или myobj = MyObject(n=7)
и myiter = iter(myobj)
, если вам нужны и "объект", и итератор. То, что вы описываете, довольно необычно, я бы сказал, что лучше делать это обычным способом (обрабатывая итераторы без итераторов и их итераторы отдельно).
@WHoZ Опять же, такие модификации в итераторе в любом случае уже необычны ...
Под интерактивностью я подразумеваю "obj = MyObject(n=7); next(obj); print(obj)" вместо использования циклов for. К сожалению, я не могу вызвать next() на итераторе (как вы сделали).
@WHoZ Вы могу вызываете next()
итератор. Но в моем первом решении obj
не является итератором. Просто итерируемый. Теперь добавлено второе решение, которое должно достичь того, чего вы хотите.
Это очень хорошее решение, но я также хотел сохранить контроль над процессом итерации, вызывая next() вручную и в интерактивном режиме. Если я не найду элегантной альтернативы, я сохраню ваше предложение, так как оно пока лучшее.