[Python-ideas] How assignment should work with generators?
Steven D'Aprano
steve at pearwood.info
Wed Nov 29 23:43:49 EST 2017
On Wed, Nov 29, 2017 at 03:08:43PM -0800, Brendan Barnwell wrote:
> On 2017-11-29 14:21, Greg Ewing wrote:
> >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.
At the point that you are conjuring from thin air an invisible suitcase
that is an exact clone of the original suitcase, in order to unpack the
clone without disturbing the original, I think the metaphor is so far
from the real-world unpacking of suitcases that it no longer applies.
Besides, it's not even correct to say that an invisible suitcase
(iterator) is constructed.
# Python 3.5
py> dis.dis("a, b, c = [97, 98, x]")
1 0 LOAD_CONST 0 (97)
3 LOAD_CONST 1 (98)
6 LOAD_NAME 0 (x)
9 ROT_THREE
10 ROT_TWO
11 STORE_NAME 1 (a)
14 STORE_NAME 2 (b)
17 STORE_NAME 3 (c)
20 LOAD_CONST 2 (None)
23 RETURN_VALUE
Before iterators existed, Python had sequence unpacking going back to at
least Python 1.5 if not older, so even if Python used a temporary and
invisible iterator *now* that has not always been the case and it might
not be the case in the future or in alternate interpreters.
Even if Python *sometimes* builds a temporary and invisible iterator, it
doesn't do it all the time, and when it does, it is pure implementation,
not interface. The interpreter is free to do whatever it likes behind
the scenes, but there's no iterator involved in the high-level Python
statement:
a, b, c = [1, 2, 3]
That code involves a list and three assignment targets, that is all.
> 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.
No, the actual thing *I* am unrolling is exactly what I write in my
code, which is the list [1, 2, 3].
I don't care what the Python interpreter iterates over internally, so
long as the results are the same. It can use an iterator, or unroll the
loop at compile-time, or turn it into recursion over a linked list for
all I care.
As much as possible, we should avoid thinking about implementation
details when trying to understand high-level semantics of Python code.
Otherwise, our mental models become obsolete when the implementation
changes.
Or worse, we become incapable of thinking about better implementations
(or better high-level semantics!) because the current implementation is
so entwined with our understanding of the high-level semantics of the
code. If we had allowed the old sequence protocol implementation to take
priority over the abstract, high-level concept of iteration, we wouldn't
have iterators today.
--
Steve
More information about the Python-ideas
mailing list