1

I believe I answered my own question while trying to figure out how to ask it properly, but since I found the answer in Python's documentation to be rather opaque, I thought I'd still post the question here.

While trying to understand the rules of single-line assignments in Python, I came across some examples that contradict the usual statement that "single-line assignments to multiple variables all take place at once".

a = 0 # variable 'a' must be assigned first

a, b = 1, a # a, b :: 1, 0

x = [2, 3, 4, 5, 6]

i, x[i] = 4, 99 # i, x[4] :: 4, 99; variable 'i' does not need to have been previously assigned

c = 8 # variable 'c' must be assigned first

c, d = 9, print(c) # c, d :: 9, None; prints 8

My confusion had to do with the fact that Python reassigned 'i' (i.e., the list index) first, before assigning 99 to index 4 of list 'x'.

While Python's documentation directly addresses this issue as following,

Although the definition of assignment implies that overlaps between the left-hand side and the right-hand side are ‘simultaneous’ (for example a, b = b, a swaps two variables), overlaps within the collection of assigned-to variables occur left-to-right, sometimes resulting in confusion.

I did not understand what it meant by "overlaps within the collection of assigned-to variables".

I see now that the parser will check to see if the indexing value for a given List has been reassigned before it assigns a new value to that index of the list.


Notes:

This is confirmed by the fact that, in this case, 'i' did not need to be assigned first before it was used as a variable index, while for 'a' it was necessary (otherwise, Python would throw an error).

For those curious, here's the PythonTutor visualization. Unfortunately, because it executes the assignment in one line (as it should), one cannot really see how Python interprets the statement.

This opacity of execution would be further compounded in the case of a user who had previously assigned i to an integer, and intended to use that previous integer as an index, and not the new value.


By the way, this is my first time asking a question, so I don't have the necessary reputation to post my own answer. Please feel free to provide any constructive advice on how I can improve any future questions I may ask, or on how I might better contribute to this community. Thanks.

3 Answers3

0

Maybe this is an English question?

overlaps within the collection of assigned-to variables occur left-to-right

So, the LHS is "the collection of assigned-to variables". In other words, "assigned-to variables" are the variables that will be assigned a value, thus the LHS.

"Overlaps within LHS" means that the object that one variable refers to depends on another. In your example, the object that c[i] refers to depends on i, so

i, c[i] = 4, 99

is run from left to right.

However, in the next test:

c, d = 9, print(c)

Since the LHS variables c and d does not overlap (depend on each other), the assignment to c and d are parallel.

iBug
  • 35,554
  • 7
  • 89
  • 134
0

It's basically saying that if, on the left hand side of your assignment statement, you have values that are repeated, in this case i, then the assigning of values will take place left-to-right, so you start with the left-most value (i), assign it, then assign the next one (x[i]), and so on.

Edit: @iBug said it better.

CoffeeTableEspresso
  • 2,614
  • 1
  • 12
  • 30
0

The easiest way to think about this is that the right hand side (RHS) gets evaluated first and then assigned to the left hand side (LHS).

Note that when the RHS has commas in it, it is actually a tuple. When the LHS is a tuple or list, then it is treated as a special syntax, in which the RHS will be unpacked and assigned to the variables on the LHS.

So when the LHS is a tuple or list of variables, the process goes like this:

  1. construct RHS, starting with first element of tuple and continuing to last
  2. unpack the RHS tuple and assign elements one-by-one to the objects on the LHS, from left to right.

In other words, behind the scenes Python turns

(a, b, c) = (x(), y(), z())

into

buffer = (x(), y(), z())
a = buffer[0]
b = buffer[1]
c = buffer[2]

There's also some more information on this at https://stackoverflow.com/a/16409962/3830997 .

Note that this works with or without the parentheses (you don't need parentheses to create a tuple), and it works with either a tuple or list on the LHS and any iterable on the RHS. Also, the variables named on the LHS don't have to exist in advance (a clear sign that the LHS never gets assembled into a normal tuple).

Matthias Fripp
  • 17,670
  • 5
  • 28
  • 45