Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning object before modifying it in __next__()

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.

like image 917
WHoZ Avatar asked Feb 23 '26 18:02

WHoZ


1 Answers

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
like image 179
Kelly Bundy Avatar answered Feb 26 '26 07:02

Kelly Bundy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!