[Python-ideas] PEP 479: Change StopIteration handling inside generators

Wolfgang Maier wolfgang.maier at biologie.uni-freiburg.de
Fri Nov 21 14:31:33 CET 2014


On 21.11.2014 12:24, Raymond Hettinger wrote:
>
>> On Nov 15, 2014, at 1:29 AM, Chris Angelico
>> <rosuav at gmail.com
>> <mailto:rosuav at gmail.com>> wrote:
>>
>> Abstract
>> ========
>>
>> This PEP proposes a semantic change to ``StopIteration`` when raised
>> inside a generator, unifying the behaviour of list comprehensions and
>> generator expressions somewhat.
>
>
> Please let me know if I'm reading the PEP correctly.
> Does the proposal break all existing code in generators
> that uses next() to raise StopIteration or that raises
> StopIteration explicitly?
>
> For example, here is the pure python recipe for itertools.accumulate()
> show in the docs at
> https://docs.python.org/3/library/itertools.html#itertool-functions :
>
>
>      def accumulate(iterable, func=operator.add):
>          'Return running totals'
>          # accumulate([1,2,3,4,5]) --> 1 3 6 10 15
>          # accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120
>          it = iter(iterable)
>          total = next(it)
>          yield total
>          for element in it:
>              total = func(total, element)
>              yield total
>
> Or would it break the traditional examples of how to write something
> like izip() using a generator?
>
>      def izip(iterable1, iterable2):
>          it1 = iter(iterable1)
>          it2 = iter(iterable2)
>          while True:
>              v1 = next(it1)
>              v2 = next(it2)
>              yield v1, v2
>
>      assert list(izip('ab', 'cde')) == [('a', 'c'), ('b', 'd')]
>      assert list(izip('abc', 'cd')) == [('a', 'c'), ('b', 'd')]
>

Since I already learnt quite a lot from following this thread:
I checked yesterday what the docs have to say about the pure-python 
equivalent of python3's zip() because I expected it to look like the 
above izip recipe (making it incompatible with the PEP behavior). 
However, I found that the given equivalent code is:

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.e., there is no unprotected next call in this example.

What surprised me though is that the protection here is done via the 
default argument of next() while more typically you'd use a try/except 
clause. So what's the difference between the two ? Specifically, with a 
default value given will next just catch StopIteration, which you could 
do less verbosely yourself and/or is there some speed gain from the fact 
that the Error has to be propagated up one level less ?
Is there a guideline when to use try/except vs. next with a default value ?

Thanks,
Wolfgang



More information about the Python-ideas mailing list