I'm programming an iterator object that returns modifications of itself and I want the original object to be part of the iteration process, so it needs to be returned before being modified.
...but:
- Creating a deepcopy of the object to return is very time-consuming.
- The object have lots of attributes, so defining auxiliary variables doesn't seem an elegant solution.
yield
inside__next__
will not work because it returns a different type (generator) in each iteration instead.- I could use
yield
inside__iter__
to get an iterator but then I couldn't use__next__
to get on-demand iterations (my object is a Turing machine, so a controlled step-by-step print of the tape would be nice).
An example of working, but inefficient, code:
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)
Output:
7
8
9
I'm probably missing something here but I can't figure it out.
CodePudding user response:
Generator seems like a good idea. Replace your __iter__
and __next__
with this:
def __iter__(self):
while not self.n >= 10:
yield self
self.n = 1
(In this particular case we could of course do while self.n < 10:
instead.)
Alternatively, to make your object itself its own iterator, as discussed in our comments below, you could use a flag:
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)
Or since iterated_original
ideally is set to True
after the original has been iterated, here's as late as I can do it:
def __next__(self):
if self.iterated_original:
self.n = 1
if self.n >= 10:
raise StopIteration
try:
return self
finally:
self.iterated_original = True