10

What would be the nice way to return something from an iterator one last time when it's exhausted. I'm using a flag, but this is rather ugly:

class Example():

    def __iter__(self):
        self.lst = [1,2,3]
        self.stop = False # <-- ugly            
        return self

    def next(self):
        if self.stop:  # <-- ugly
            raise StopIteration
        if len(self.lst) == 0:
            self.stop = True            
            return "one last time"
        return self.lst.pop()

Background: I'm fetching an unknown amount of strings from an external source and send them further down to the caller. When the process is over, I want to emit a string "x records processed". I have no control over calling code, so this must be done inside my iterator.

twasbrillig
  • 17,084
  • 9
  • 43
  • 67
georg
  • 211,518
  • 52
  • 313
  • 390

4 Answers4

6

Maybe you can use a generator function instead:

def example():
    lst = [1, 2, 3]
    while lst:
        yield lst.pop()
    yield 'one last time'
Dan Gerhardsson
  • 1,869
  • 13
  • 12
6

You could just yield from __iter__ which would turn it into a generator function (alternately you could just write a generator function as suggested by Dan). Just as a warning, this might be misleading to people that abuse the next method.

class Example():

    def __iter__(self):
        lst = [1,2,3]
        for i in reversed(lst):
            yield i
        yield "one last time"
Zach Kelling
  • 52,505
  • 13
  • 109
  • 108
1

Don't return one extra thing. That's a bad idea because it doesn't extend well. What if you want to do sum as well as count? Or hash as well as count? An iterator is a stateful object. Make use of that.

class Example( collections.Iterator ):
    def __iter__(self):
        self.lst = [1,2,3]
        self.count = 0       
        return self
    def next(self):
        if self.lst:
            self.count += 1
            return self.lst.pop()
        raise StopIteration()

Use it like this.

my_iter= iter(Example())
for item in my_iterprin:
    print( item )

print( my_iter.count )
S.Lott
  • 384,516
  • 81
  • 508
  • 779
0

You could do something like this:

class Example():
    def __iter__(self):
        self.lst = [1, 2, 3]
        return self

    def next(self):
        try:
            return self.lst.pop()
        except IndexError:
            print "done iterating"
            raise StopIteration

>>> for i in Example():
...   print i
...
3
2
1
done iterating

In your actual code you will probably need to change the exception type that you are catching, but this format should still be applicable.

Andrew Clark
  • 202,379
  • 35
  • 273
  • 306