[Tutor] comma in an assignment

eryksun eryksun at gmail.com
Wed Oct 23 03:35:06 CEST 2013


On Tue, Oct 22, 2013 at 3:20 PM, Key, Gregory E (E S SF RNA FSF 1 C)
<gregory.key at siemens.com> wrote:
> I understand that a comma in Python is a separator and not an operator. In

The comma operator creates a tuple. It has low precedence, so you
usually need parentheses. But sometimes the parentheses are redundant
such as with a return statement like `return (x, y, z)` vs `return x,
y, z`, or on the right-hand side of an assignment:

    >>> y = 1, 2, 3
    >>> y
    (1, 2, 3)


> some of the MatPlotLib examples I see code like this:
>
> line1, = ax1.plot(t, y1, lw=2, color='red', label='1 HZ')
>
> What does the comma do in an assignment statement?

For an assignment target list, CPython doesn't actually create a
tuple. It's a notational tuple used by the compiler. It's present in
the abstract syntax tree (creating an AST is an intermediate step in
the compilation process); however, the final bytecode does sequence
unpacking directly to the target names. Here's the AST for `y, = x`:

    >>> print ast.dump(ast.parse('y, = x').body[0])
    Assign(targets=[Tuple(elts=[Name(id='y', ctx=Store())], ctx=Store())],
    value=Name(id='x', ctx=Load()))

The AST Tuple in the assignment consists of one item, a Name with a
Store context. In this case, the compiler generates bytecode to unpack
a length 1 sequence and store the item to the name "y".

In practice this is pretty simple and intuitive:

    >>> y, = [1]
    >>> y
    1

    >>> x, y, z = 1, 2, 3
    >>> x, y, z
    (1, 2, 3)

You can use compile() and dis to see the bytecode itself:

    >>> dis.dis(compile('y, = x', '', 'exec'))
      1           0 LOAD_NAME                0 (x)
                  3 UNPACK_SEQUENCE          1
                  6 STORE_NAME               1 (y)
                  9 LOAD_CONST               0 (None)
                 12 RETURN_VALUE

The argument in UNPACK_SEQUENCE(1) is the number of items to unpack to
the frame's stack. For stack-based operations, think of using an RPN
calculator. An operator -- in this case a bytecode instruction -- pops
its operands off the stack and then pushes the result back on in their
place. The more complex the expression, the bigger the stack needs to
grow (think of a stack of plates) in order to hold temporary results.

Unpacking can also handle more complex structures such as `t, (x, y) = z`:

    >>> z = [1, [2, 3]]
    >>> t, (x, y) = z
    >>> t, x, y
    (1, 2, 3)

If you prefer, you can use square brackets on the left-hand side. The
compiled bytecode is the same, whether the left-hand side has Tuple or
List types in the AST:

    >>> [t, [x, y]] = z
    >>> t, x, y
    (1, 2, 3)

Bytecode comparison:

    >>> dis.dis(compile('t, (x, y) = z', '', 'exec'))
      1           0 LOAD_NAME                0 (z)
                  3 UNPACK_SEQUENCE          2
                  6 STORE_NAME               1 (t)
                  9 UNPACK_SEQUENCE          2
                 12 STORE_NAME               2 (x)
                 15 STORE_NAME               3 (y)
                 18 LOAD_CONST               0 (None)
                 21 RETURN_VALUE

    >>> dis.dis(compile('[t, [x, y]] = z', '', 'exec'))
      1           0 LOAD_NAME                0 (z)
                  3 UNPACK_SEQUENCE          2
                  6 STORE_NAME               1 (t)
                  9 UNPACK_SEQUENCE          2
                 12 STORE_NAME               2 (x)
                 15 STORE_NAME               3 (y)
                 18 LOAD_CONST               0 (None)
                 21 RETURN_VALUE

After the first unpack, item 0 is popped off the stack and stored to
t. Next item 1 (a length 2 sequence) is popped, unpacked, and stored
to x and y.


More information about the Tutor mailing list