Home > Blockchain >  Swapping variables and list elements in Python works unexpectedly
Swapping variables and list elements in Python works unexpectedly

Time:01-11

I have just came up with an example of code:

ls = [2222, 1111]

a = ls[0]
print(a is ls[0])

a, ls[1] = ls[1], a
print(ls)

which prints:

True
[2222, 2222]

My question is, why isn't a the same object as ls[0] in the above case? The a is ls[0] check is True, therefore it must be the same as:

ls[0], ls[1] = ls[1], ls[0]

but it isn't. The latter one produces [1111, 2222]. What is this mYsTeRy?

CodePudding user response:

Simple assignments have no affect on the previous value of the target.

After

ls = [2222, 1111]

a = ls[0]

both a and the first element of ls are references to the integer 2222.

The assignment

a, ls[1] = ls[1], a

is effectively the same as

t = ls[1], a  # The tuple (1111, 2222)
a = t[0]  # Set a to t[0] == 1111
ls[1] = t[1]  # Set ls[1] to t[1] == 2222

At no point have you modified the first element of ls; you've only changed what a refers to and what the second element of ls refers to. You can see that a is now refers to 1111, since that's what the value of ls[1] was before ls[1] was modified.

>>> print(a)
1111

CodePudding user response:

You are not setting ls[0]!

Fixing it:

ls = [2222, 1111]

# Assigns ls[0] to a, but not changes ls[0]!
a = ls[0]
print(a is ls[0])

# Assigns ls[1] to a and a to ls[1]. At this point ls[1] is a!
a, ls[1] = ls[1], a
print(ls)

# Now ls[0] is assigned!
ls[0] = a
print(ls)

Outputs:

True
[2222, 2222]
[1111, 2222]

BTW: You can do the reverse listing with:

ls = [2222, 1111]
print(ls[::-1])

Outputs:

[1111, 2222]

CodePudding user response:

Taken literally, neither a nor ls[1] are objects: They are an identifier and subscription, respectively. While they evaluate to objects when used in an expression, that does not make them identical to these objects – and the identity of "their" objects does not extend to them, either.

  • The expression a is ls[0] means "evaluate a and ls[0] and compare the result for identity". It does not check whether the identifier a is identical to the subscription ls[0].

Notably, when used in an assignment statement, both a and ls[1] do not represent "an expression to look up an object" but rather "a target to assign an object to". The features of the former do not impact the latter.


The statement a, ls[1] = ls[1], a uses both a and ls[1] as both expressions and targets:

  • The right hand side is an expression. It represents "a tuple of the results of evaluating ls[1] and a".

  • The left hand side is an assignment. It represents "assign to the identifier a and the subscription ls[1]".

Assignment to an identifier always is immediate: it does not matter what value the identifier previously referred to. Specifically, it does not matter that a previously referred to an object that was also referred to by ls[0], and it will not matter that its new referent was also referred to by ls[1].


To understand why this distinction is important, consider the case that an identifier "is identical to" a literal:

>>> # integers < 256 are interned
>>> a = 12
>>> # identifier "is identical to" literal
>>> a is 12
True

If identity in expressions would equate to identity in assignments, then setting a = 13 would now reassign both a and 12 to have the value 13.

CodePudding user response:

This code would not work as in this case you have a reference to the object at ls[0] and not a reference to the memory position of ls[0].

  •  Tags:  
  • Related