[Python-ideas] Control Flow - Never Executed Loop Body

Steven D'Aprano steve at pearwood.info
Tue Mar 22 09:28:28 EDT 2016


On Tue, Mar 22, 2016 at 01:27:05PM +0100, Sven R. Kunze wrote:

[...]
> >>>>def nope():
> >...     item = sentinel = object()
> >...     for item in locals().values():
> >...         print("We have:", item)
> >...     if item is sentinel:
> >...         print("We have no items.")
> >...
> >>>>nope()
> >We have: <object object at 0x7f17b1c6b0a0>
> >We have: <object object at 0x7f17b1c6b0a0>
> >We have no items.
> >An iterator can return *any* *object*. That's why Python uses an
> >exception (StopIteration) to signal the absence of an object.
> 
> Oh, you are right. That's definitely an ugly edge case. Any proposed 
> solution should just work given the simplicity of the problem.

I think we're starting to give far more weight to that case than it 
deserves. It doesn't deserve a note in the docs, let alone new syntax to 
"solve it". Nobody is going to do it, except to win a bet. Certainly 
nobody is going to do it *accidently*.

> >But now we're getting into the realm of ugly code to deal with edge
> >cases. Like with "yield from", language support can be justified when
> >there's a simple and obvious *but imperfect* way to do something
> >("yield from x" is not the same as "for item in x: yield item").
> 
> Exactly. Despite the fact that "yield from" is shorter it can handle 
> edge cases more beautiful.

For plain old iteration, "yield from" does nothing more than iterating 
over the iterable and yielding. There's no "edge cases" here, there are 
some pretty major differences. As the PEP says:


    If yielding of values is the only concern, this can be performed 
    without much difficulty using a loop such as

    for v in g:
        yield v

    However, if the subgenerator is to interact properly with the caller 
    in the case of calls to send() , throw() and close() , things become 
    considerably more difficult. 

https://www.python.org/dev/peps/pep-0380/

"yield from" doesn't fix some weird edge case with iteration. It 
provides some significant new functionality which is *very* tricky to 
get right. That's not the case here with detecting an empty iterator. 
There are two perfectly adequate and simple ways to detect an empty 
iterator:

# 1
empty = True
for x in iterable:
    empty = False
    do_stuff()

if empty:
   handle_empty()


#2
x = sentinel = object()
assert iterable is not locals().values()  # *wink*
for x in iterable:
    do_stuff()

if x is sentinel:
    handle_empty()


-- 
Steve


More information about the Python-ideas mailing list