Unpacking: [x,y], (x,y), x,y - what is the difference?

As the other answer has explained, there is no semantic difference. The only reason to prefer one form over another is visual clarity; x, y = f() is usually neater, but something like [(id,)] = do_sql_query() may sometimes be written to indicate that the result is expected to be a list containing a tuple. The assignment target [(id,)] is semantically equivalent to (id,), but it communicates something more to the human reader.


There is no difference. Regardless of what kind of syntactic sequence you use, the same byte code is generated.

>>> def f():
...   return 0, 1
...
>>> import dis
>>> dis.dis('[a,b] = f()')
  1           0 LOAD_NAME                0 (f)
              2 CALL_FUNCTION            0
              4 UNPACK_SEQUENCE          2
              6 STORE_NAME               1 (a)
              8 STORE_NAME               2 (b)
             10 LOAD_CONST               0 (None)
             12 RETURN_VALUE
>>> dis.dis('(a,b) = f()')
  1           0 LOAD_NAME                0 (f)
              2 CALL_FUNCTION            0
              4 UNPACK_SEQUENCE          2
              6 STORE_NAME               1 (a)
              8 STORE_NAME               2 (b)
             10 LOAD_CONST               0 (None)
             12 RETURN_VALUE
>>> dis.dis('a, b = f()')
  1           0 LOAD_NAME                0 (f)
              2 CALL_FUNCTION            0
              4 UNPACK_SEQUENCE          2
              6 STORE_NAME               1 (a)
              8 STORE_NAME               2 (b)
             10 LOAD_CONST               0 (None)
             12 RETURN_VALUE

In every case, you simply call f, then use UNPACK_SEQUENCE to produce the values to assign to a and b.


Even if you want to argue that byte code is an implementation detail of CPython, the definition of a chained assignment is not. Given

x = [a, b] = f()

the meaning is the same as

tmp = f()
x = tmp
[a, b] = tmp

x is assigned the result of f() (a tuple), not the "list" [a, b].


Finally, here is the grammar for an assignment statement:

assignment_stmt ::=  (target_list "=")+ (starred_expression | yield_expression)
target_list     ::=  target ("," target)* [","]
target          ::=  identifier
                     | "(" [target_list] ")"
                     | "[" [target_list] "]"
                     | attributeref
                     | subscription
                     | slicing
                     | "*" target

Arguably, the "[" [target_list] "]" could and should have been removed in Python 3. Such a breaking change would be difficult to implement now, given the stated preference to avoid any future changes to Python on the scale of the 2-to-3 transition.