Home > front end >  Returning object before modifying it in __next__()
Returning object before modifying it in __next__()

Time:05-12

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
  • Related