elegent
elegent

Reputation: 4007

How to handle an exhausted iterator?

While searching the Python Documentation I found the equivalent python implementation of Pythons build-in zip() function.

Instead of catching a StopIteration exception which signals that there are no further items produced by the iterator the author(s) use an if statement to check if the returned default value form next() equals object() ("sentinel") and stop the generator:

def zip(*iterables):
    # zip('ABCD', 'xy') --> Ax By
    sentinel = object()
    iterators = [iter(it) for it in iterables]
    while iterators:
        result = []
        for it in iterators:
            elem = next(it, sentinel)

            if elem is sentinel:
                return

            result.append(elem)
        yield tuple(result)

I wonder now if there is any difference between the exception catching or an if statement as used by the Python Docs?

Or better, as @hiro protagonist pointed out:
What's wrong with using a try statement considering EAFP (Easier to ask for forgiveness than permission) in Python?

def zip(*iterables):
    # zip('ABCD', 'xy') --> Ax By
    iterators = [iter(it) for it in iterables]
    while iterators:
        result = []
        for it in iterators:

            try:
                elem = next(it)
            except StopIteration:
                return

            result.append(elem)
        yield tuple(result)

Also as Stoyan Dekov mentioned "A try/except block is extremely efficient if no exceptions are raised. Actually catching an exception is expensive." (see the docs for more information)
But an exception would only occur once, namely as soon as the iterator is exhausted. So exception handling would be the better solution in this case?

Upvotes: 23

Views: 5839

Answers (3)

hiro protagonist
hiro protagonist

Reputation: 46849

You mean as opposed to this?

def zip2(*iterables):
    # zip('ABCD', 'xy') --> Ax By
    iterators = [iter(it) for it in iterables]
    while iterators:
        result = []
        for it in iterators:
            try:
                elem = next(it)
            except StopIteration:
                return

            result.append(elem)
        yield tuple(result)

interesting question... i'd have preferred this alternative version - especially considering EAFP (Easier to ask for forgiveness than permission.)

even if try/except is slower than the if statement; this happens once only - as soon as the first iterator is exhausted.

it may be worth noting that this is not the actual implementaion in python; just an implementation that is equivalent to the real implementation.


UPDATE according to comments:

note that PEP 479 suggests to return from the generator and not raise StopIteration.

Upvotes: 9

Ethan Furman
Ethan Furman

Reputation: 69031

When you see equivalent python code in the docs the goal of such code is to be easy to understand. if is easy to understand, while try/except is a higher-level construct.

Functionally, there is no difference. Performance-wise, there may be a small, but probably insignificant, difference.

As far as actual coding goes, different people think in different ways, so use whichever method makes more sense to you. The times when LBYL and EAFP actually make a difference is when a race condition can exist -- and there is no such condition here.

Upvotes: 1

SD.
SD.

Reputation: 9549

Generally raising exceptions is always considered an expensive operation in any programming language. There are plenty of websites to read why is that and I'm not going to go into details on what it involves.

From Python Docs.

A try/except block is extremely efficient if no exceptions are raised. Actually catching an exception is expensive.

Both using if/else and try/catch has its advantages and disadvantages depending on the situation.

  • For example try/catch is used mostly for cases where an exception is a rare event (e.g. the code will succeed in almost all cases).
  • In your example you know the loop will throw an exception every time it is invoked, which makes it very inefficient to use try/catch

Upvotes: 6

Related Questions