[Tutor] "=" invalid syntax ? [Assignment grammar stuff] (fwd)

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Tue Feb 3 02:58:50 EST 2004


> Do you know, off the top of your head, any other special cased things
> like that.

Hi orbitz,

Nope...  Can't remember a thing.  *grin* Thank goodness for Google.


> Another one someone showed me was:
>
> x, y = [1,2]

Yes, this works too, because [1, 2] is treated as a sequence.  Here's the
official word from the reference docs:

"""If the target is a target list enclosed in parentheses or in square
brackets: The object must be a sequence with the same number of items as
there are targets in the target list, and its items are assigned, from
left to right, to the corresponding targets."""

    (http://www.python.org/doc/ref/assignment.html)


So that part of the definition covers the case when we say:

    x, y = [1, 2]


Hmmm... I wonder, though...

###
>>> def test_iterator():
...     yield 1
...     yield 2
...
>>> x, y = test_iterator()
>>> x
1
>>> y
2
###


It looks like tuple unpacking works even on iterators.  If we know exactly
how many things are coming out of an iteration, we can use tuple
assignment.  Not that we'd ever do this, but still neat.  *grin*



> Also, in an lvalue, if i do
>
> (x, y) = [1,2]
>
> The () don't make it a tuple in such a situtaion do they?

The documentation implies that it treats the left hand side as a bunch of
targets, so I don't think it constructs an intermediate tuple with them.



But that's just a guess.  We can check this!

###
>>> l = [1, 2]
>>> def t1():
...     x = l[1]
...     y = l[2]
...
>>> def t2():
...     x, y = l[1], l[2]
...
>>> def t3():
...     x, y = l
...
>>> import dis
###


The 'dis' module provides a very low-level "disassembly" view of what
Python really thinks a function is doing.  Here's some sample output:

###
>>> dis.dis(t1)
  2           0 LOAD_GLOBAL              0 (l)
              3 LOAD_CONST               1 (1)
              6 BINARY_SUBSCR
              7 STORE_FAST               1 (x)

  3          10 LOAD_GLOBAL              0 (l)
             13 LOAD_CONST               2 (2)
             16 BINARY_SUBSCR
             17 STORE_FAST               0 (y)
             20 LOAD_CONST               0 (None)
             23 RETURN_VALUE
>>> dis.dis(t2)
  2           0 LOAD_GLOBAL              0 (l)
              3 LOAD_CONST               1 (1)
              6 BINARY_SUBSCR
              7 LOAD_GLOBAL              0 (l)
             10 LOAD_CONST               2 (2)
             13 BINARY_SUBSCR
             14 BUILD_TUPLE              2
             17 UNPACK_SEQUENCE          2
             20 STORE_FAST               1 (x)
             23 STORE_FAST               0 (y)
             26 LOAD_CONST               0 (None)
             29 RETURN_VALUE
>>> dis.dis(t3)
  2           0 LOAD_GLOBAL              0 (l)
              3 UNPACK_SEQUENCE          2
              6 STORE_FAST               1 (x)
              9 STORE_FAST               0 (y)
             12 LOAD_CONST               0 (None)
             15 RETURN_VALUE
###


You don't have to understand everything here.  But just from casual
comparison, we can see that the disassembly of t1() and t3() shows that
Python isn't constructing a tuple, so we can definitively say that tuple
assignment doesn't make a tuple for the lvalue.  Whew;  I had felt a
little uncomfortable about guessing.  *grin*


By the way, in t2(), Python does construct a tuple, but that's because
Python's evaluating the right hand side of:

    x, y = l[1], l[2]

and constructing the tuple

    (l[1], l[2])

before doing the tuple assignment.  So Python has to fully evaluate the
right hand side before doing assignments, and if it means it needs to
create an intermediate tuple, it does so.

This explains why we can do swapping like this:

   x, y = y, x

because Python first creates an intermediate tuple with the values of 'y'
and 'x', and then assigns that rvalue tuple to 'x' and 'y'.



Hope this helps!




More information about the Tutor mailing list