[Python-ideas] Change how Generator Expressions handle StopIteration

Ron Adam ron3200 at gmail.com
Thu Nov 6 04:34:05 CET 2014



On 11/05/2014 03:47 PM, Nick Coghlan wrote:
>  > > I'm sorry if this invalidates Nick's endorsement of the proposal.
>  >
>  > I actually thought this was what you meant originally, and while it
> *would* require changes to contextlib, they're fairly minor: the current
> check for StopIteration would change to catch "UnexpectedStopIteration"
> instead, and the exception identity check would look at __cause__ rather
> than directly at the caught exception.
>  >
>  > > I definitely see this as a serious backward incompatibility: no matter
> how often it leads to buggy or obfuscated code, it's how things have always
> worked.
>  >
>  > Aye, breaking the equivalence between "return" and "raise StopIteration"
> is pretty major.
>  >
>  > I'm not even sure a plausible transition plan is possible, as at least
> contextlib would trigger any warning we might issue.
>
> And having said that... what if we introduced UnexpectedStopIteration but
> initially made it a subclass of StopIteration?
>
> We could issue a deprecation warning whenever we triggered the
> StopIteration -> UnexpectedStopIteration conversion, pointing out that at
> some point in the future (3.6? 3.7?), UnexpectedStopIteration will no
> longer be a subclass of StopIteration (perhaps becoming a subclass of
> RuntimeError instead?).
>
> contextlib could avoid the warning by preconstructing a suitable
> UnexpectedStopIteration instance and throwing *that* into the generator,
> rather than throwing in a StopIteration raised from the with statement body.

I really don't like the name Unexpected anything.  It makes me think of 
blue screens and page faults.  :-/

I'm also not sure how it's supposed to work or where it should come from.


As near as I can tell these two examples below are equivalent.  I think the 
thing that needs to be avoided is the case of the endless loop.  It would 
be better to let the Exception be noisy.

How would the second example be changed in order to do that?  Or is there 
some other thing that needs to be fixed?

Cheers,
    Ron



def izip(*args):
     iters = [iter(obj) for obj in args]
     while True:
         yield list(next(it) for it in iters)  #StopIteration suppressed
                                               #by list comprehension.
                                               #resulting in empty lists.
     #While Loop never exits.
     print("never printed")

a = izip([1,2],[3,4])
print(next(a),next(a),next(a))
# (1, 3) (2, 4) ()

#list(izip([1,2],[3,4])) #Currently never returns




def izip2(*args):
     iters = [iter(obj) for obj in args]
     while True:
         L = []
         for it in iters:
             try:
               obj = next(it)       # StopIteration suppressed here.
             except StopIteration:
               break                # exit for-loop
                                    # "return" instead of "break"?
             L.append(obj)
         yield L
     # While Loop never exits.
     print("never printed")

a = izip2([5,6],[7,8])
print(next(a),next(a),next(a))
# (5, 7) (6, 8) ()

list(izip2([5,6],[7,8])) #Currently never returns

# Not only doesn't it exit, but it's building
# an endless list of empty lists!





More information about the Python-ideas mailing list