69

In Python one can do:

a, b   = 1, 2
(a, b) = 1, 2
[a, b] = 1, 2

I checked the generated bytecode using dis and they are identical.
So why allow this at all? Would I ever need one of these instead of the others?


See also Multiple assignment and evaluation order in Python regarding the basic semantics of this kind of assignment.

See also Understand Python swapping: why is a, b = b, a not always equivalent to b, a = a, b? for more complex cases, where the order of assignment matters.

1
  • 33
    +1 for checking the generated bytecode Commented Mar 3, 2011 at 15:22

5 Answers 5

75

One case when you need to include more structure on the left hand side of the assignment is when you're asking Python unpack a slightly more complicated sequence. E.g.:

# Works
>>> a, (b, c) = [1, [2, 3]]

# Does not work
>>> a, b, c = [1, [2, 3]]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: need more than 2 values to unpack

This has proved useful for me in the past, for example, when using enumerate to iterate over a sequence of 2-tuples. Something like:

>>> d = { 'a': 'x', 'b': 'y', 'c': 'z' }
>>> for i, (key, value) in enumerate(d.iteritems()):
...     print (i, key, value)
(0, 'a', 'x')
(1, 'c', 'z')
(2, 'b', 'y')
Sign up to request clarification or add additional context in comments.

2 Comments

Better: use an OrderedDict and i will have even more meaning.
As of python 3.7, dictionaries are (insertion) ordered, so ordered dictionaries are not needed.
13

Python tuples can often be written with or without the parentheses:

a = 1, 2, 3

is equivalent to

a = (1, 2, 3)

In some cases, you need parentheses to resolve ambiguities, for examples if you want to pass the tuple (1, 2) to the function f, you will have to write f((1, 2)). Because the parentheses are sometimes needed, they are always allowed for consistency, just like you can always write (a + b) instead of a + b.

If you want to unpack a nested sequence, you also need parentheses:

a, (b, c) = 1, (2, 3)

There does not seem to be a reason to also allow square brackets, and people rarely do.

4 Comments

Would you consider it as a bug when square brackets are allowed in this context? For me this behavior seems to violate (at least): There should be one-- and preferably only one --obvious way to do it. and Special cases aren't special enough to break the rules. Thanks
@eat: I definitely would not call this a bug. It is clearly documented. I'm just wondering about the rationale for this design decision.
OK, not a bug, but when you say that just wondering about the rationale for this design decision makes me also curious and perplexed of what would be a (even single) justifiable use case for [...]= then?. Thanks
The square bracket syntax makes it more straightforward to use list methods when enumerating over a sequence of collections. Admittedly a narrow use case, since the collections have to be of equal length to be unpacked this way.
4

When unpacking a single-element iterable, the list syntax is prettier:

a, = f()    # comma looks out of place
(a,) = f()  # still odd
[a] = f()   # looks like every other list

5 Comments

I'm led to believe a list is heavier to generate so a tuple is quicker. This may not be significant or even true but anyway I'm used to trailing commas now (and kind of like them in a wierd way). I've edited to black standards because we can't really assess appearance properly all crushed up like that (although black actually wraps the first form with parentheses to appear like the second, anyway).
@NeilG: What list? It’s just syntax: the variables to which you are assigning aren’t actually in any list object (what would it contain?).
Maybe I'm ignorant of the Python compiler, Davis, I hope you're about to enlighten me. Silly me thought that the square brackets in the third form would construct a list during assignment of a and then immediately throw it away again?
@NeilG: The question already said that these all byte-compile to the same instructions. The size of the “list” is always known at compile time, so the interpreter just directly unpacks the right-hand side into however many elements (even 1).
Of course, how did I miss that? Thank you. I think I'm going to adopt your practice in these circumstances from now on.
1

They are also same because the assignment happens from Right to left and on the right, you have one type, which is a sequence of two elements. When the asignment call is made, the sequence is unpacked and looked for corresponding elements to match and given to those values. Yes, any one way should be fine in this case where the sequence is unpacked to respective elements.

2 Comments

Are you implying that there are cases where these would work differently?
In the case, When the LHS is a single identifier, then there is only one STORE_FAST call is made. Otherwise it is all same.
0

An open parenthesis allows for a multi-line assignment. For example, when reading a row from csv.reader(), it makes code more readable (if less efficient) to load the list into named variables with a single assignment.

Starting with a parenthesis avoids long or \ escaped lines.

(a, b,
c) = [1, 2, 3]

(Imagine more and longer variable names)

3 Comments

What’s the inefficiency you mention?
Mostly just the filling up of namespace with those variable names. I know it's not much of a burden, but I came from an embedded background.
CPython compiles a function’s local variables into slots in an array; it’s probably rather more efficient than accessing the sequence’s elements by index (especially repeatedly).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.