[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