[Python-ideas] How assignment should work with generators?
brenbarn at brenbarn.net
Wed Nov 29 18:08:43 EST 2017
On 2017-11-29 14:21, Greg Ewing wrote:
> C Anthony Risinger wrote:
>> `a, b, ...` to me says "pull out a and b and throw away the rest"...
> > The mere presence of more
>> characters (...) implies something else will *happen* to the remaining
>> items, not that they will be skipped.
> It seems that many people think about unpacking rather
> differently from the way I do. I think the difference
> is procedural vs. declarative.
> To my way of thinking, something like
> a, b, c = x
> is a pattern-matching operation. It's declaring that
> x is a sequence of three things, and giving names to
> those things. It's not saying to *do* anything to x.
> With that interpretation,
> a, b, ... = x
> is declaring that x is a sequence of at least two
> items, and giving names to the first two. The ellipsis
> just means that there could be more items, but we
> don't want to give them names.
> On the other hand, some people seem to be interpreting
> the word "unpack" as in "unpack a suitcase", i.e. the
> suitcase is empty afterwards. But unpacking has never
> meant that in Python! If it did, then
> x = [1, 2, 3]
> a, b, c = x
> would leave x ==  afterwards.
> The only case where unpacking behaves like that is when
> the rhs is an iterator rather than a sequence, in which
> case a side effect is unavoidable. The question then is
> what the side effect should be.
That's an interesting analysis, but I don't think your view is really
the right one. It *is* unpacking a suitcase, it's just that *if
necessary* the suitcase is constructed just in time for you to unpack
it. In other words, the suitcase is not the list [1, 2, 3], but an
iterator over this list. This is the same as the behavior for "for"
loops: if you do "for item in [1, 2, 3]", the actual thing you're
unrolling is an iterator over the list. In some sense the point of the
iterable/iterator distinction is to distinguish suitcases (iterators)
from things-that-produce-suitcases-on-demand (iterables). It's just
that Python syntax (very nicely) allows us to omit the explicit iter()
call. The fact that iteration is taking place is specified by the
context; that could be a for loop, or it could be multiple assignment
targets, but it's iteration all the same.
> I would argue that, since the side effect is something
> that's not really wanted, it should be as *small* as
> possible. By that argument,
> a, b, ... = some_iterator
> should do as *little* as possible to fulfill what's
> being asked, i.e. give names to the first two items
> produced by the rhs. Consuming those two items is
> unavoidable, but there's no need to consume any more.
I see your point, but I think that middle ground doesn't really give
the benefits of either. If you expect your suitcase to remain unopened,
it's pretty cold comfort to find that someone has opened it and taken
only your pants and shoes but left the rest. If the side effect isn't
wanted, you really need the RHS to be something that isn't affected
(i.e., a re-iterable). It does seem that in some cases you may want the
iterator to be exhausted, and in others not, but I don't think it's a
good idea to try to "hide" the unpacking by limiting the number of
iterations. The important difference is between any irreversible
unpacking at all, and none at all.
"Do not follow where the path may lead. Go, instead, where there is no
path, and leave a trail."
More information about the Python-ideas