[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