436

In Python, I've seen two variable values swapped using this syntax:

left, right = right, left

Is this considered the standard way to swap two variable values or is there some other means by which two variables are by convention most usually swapped?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
WilliamKF
  • 41,123
  • 68
  • 193
  • 295
  • 2
    @eyquem: it simply comes down to whether [tag:order-of-evaluation] is defined by the language for a tuple/list assignment. Python does, most older languages don't. – smci Aug 18 '18 at 00:37
  • Hrmm C++ has swap(a[i], a[k]) why can't we have something like this for Python. – Nils Aug 24 '19 at 19:28
  • 3
    @Nils Because in Python, assignment is an **aliasing** operation while in C++ assignment *to a reference* is a **replacement** operation. Therefore in Python you cannot replace the values of the arguments passed to a function like in C++ (you can only mutate them). See [*Copying and Comparing: Problems and Solutions*](https://www.researchgate.net/publication/221496499_Copying_and_Comparing_Problems_and_Solutions) by Grogono and Sakkinen for an explanation of these terms. – Géry Ogam Oct 08 '20 at 13:19

8 Answers8

519

Python evaluates expressions from left to right. Notice that while evaluating an assignment, the right-hand side is evaluated before the left-hand side.

Python docs: Evaluation order

That means the following for the expression a,b = b,a :

  • The right-hand side b,a is evaluated, that is to say, a tuple of two elements is created in the memory. The two elements are the objects designated by the identifiers b and a, that were existing before the instruction is encountered during the execution of the program.
  • Just after the creation of this tuple, no assignment of this tuple object has still been made, but it doesn't matter, Python internally knows where it is.
  • Then, the left-hand side is evaluated, that is to say, the tuple is assigned to the left-hand side.
  • As the left-hand side is composed of two identifiers, the tuple is unpacked in order that the first identifier a be assigned to the first element of the tuple (which is the object that was formerly b before the swap because it had name b)
    and the second identifier b is assigned to the second element of the tuple (which is the object that was formerly a before the swap because its identifiers was a)

This mechanism has effectively swapped the objects assigned to the identifiers a and b

So, to answer your question: YES, it's the standard way to swap two identifiers on two objects.
By the way, the objects are not variables, they are objects.

eyquem
  • 26,771
  • 7
  • 38
  • 46
  • 1
    As far as I understand, swapping two variables in this way does NOT use extra memory, just memory for 2 variables themselves, am I right ? – Chau Pham May 11 '19 at 13:25
  • 5
    @Catbuilts Constructing the tuple will take up some extra memory (likely more than the 3-variable version of the swap would take up), but since the only things being swapped are memory addresses, it won't be much extra memory in the absolute sense (maybe 24 extra bytes). – Brilliand Sep 14 '19 at 07:14
  • @Brilliand: Thks. Do you have any documents for this subject. It's quite interesting and I would like to have a futher read. Thanks. – Chau Pham Sep 16 '19 at 11:35
  • 3
    @Catbuilts I'm not sure, but it might help to read up on how C++ pointers work. Whereas Python tries to automatically do things the best way for you, C++ actually gives you all the options to do things in all the possible right and wrong ways, so that's a good starting point for learning what the drawbacks are of the approach that Python takes. Also remember that having a "64-bit" operating system means that storing a memory address takes up 64 bits of memory - that's part of where I got my "24 bytes" number from. – Brilliand Sep 16 '19 at 23:19
  • 1
    Great explanation. Just adding that this is why you can also use this method to rearrange any number of "variables", e.g. `a, b, c = c, a, b`. – alelom Feb 01 '20 at 18:27
  • While this is neat, I wouldn’t use this in everyday code. The only reason to use this IMO is when you must swap the values atomically. Unlike the other solution this is Threadsafe. – Rol Mar 26 '21 at 08:10
  • @alelom, yes, in fact you can do any combination. – D.L Aug 24 '22 at 20:11
130

That is the standard way to swap two variables, yes.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
43

I know three ways to swap variables, but a, b = b, a is the simplest. There is

XOR (for integers)

x = x ^ y
y = y ^ x
x = x ^ y

Or concisely,

x ^= y
y ^= x
x ^= y

Temporary variable

w = x
x = y
y = w
del w

Tuple swap

x, y = y, x
NoOneIsHere
  • 1,054
  • 16
  • 28
  • 1
    Is the simplest and the only one that is not obfuscated. – Jorge Leitao Jan 06 '16 at 23:59
  • 27
    The XOR does not swap "variables". It swaps integer variables. (Or the few other types properly implementing XOR operator) Furthermore, since according to Rogalski's answer, the Tuple Swap is optimised in the interpreter, there is really nothing against it. Short, clear, and fast. – Rawler Jun 07 '16 at 18:13
  • XOR issue can be avoided by + - operator use, but still I feel best is a, b = b, a `code` x = x+y y = x-y x = x-y `code` – ashish Dec 27 '16 at 01:08
  • If it's about integer values, you could just use simple arithmetic to do it too – era5tone Jun 14 '21 at 10:22
36

I would not say it is a standard way to swap because it will cause some unexpected errors.

nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]

nums[i] will be modified first and then affect the second variable nums[nums[i] - 1].

Yi Huang
  • 369
  • 3
  • 2
  • 4
    You have the problem in almost any programming language, that is is not safe to use swap(a,b), if a depends on b or vice versa. For example, swap(a,b) might be expanded to: `var c=a`, `a=b`, `b=c`. And then, the last assignment will use the new value of `a` to evaluate the adress of b. – Kai Petzke Jun 11 '18 at 20:16
  • 2
    `nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]`. Would solve the problem. – Bill Cheng Sep 26 '19 at 14:57
  • 7
    @JacksonKelley Evaluation of right-hand side is safe. In `nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]`: The problem is when python does the left-hand side assignment, `nums[i]` is changed, which causes `nums[nums[i] - 1]` changed unexpected. U can imagine at first u want `nums[1],nums[2] = nums[2],nums[1]`,but after `nums[1] = nums[2]` run, you no longer have `nums[2] = nums[1]`, instead u got `nums[888] = nums[1]` . – guo Mar 05 '20 at 11:01
  • wow, meet this problem when solve https://leetcode.com/problems/first-missing-positive/description/ – Pegasus Feb 17 '22 at 13:56
10

Does not work for multidimensional arrays, because references are used here.

import numpy as np

# swaps
data = np.random.random(2)
print(data)
data[0], data[1] = data[1], data[0]
print(data)

# does not swap
data = np.random.random((2, 2))
print(data)
data[0], data[1] = data[1], data[0]
print(data)

See also Swap slices of Numpy arrays

gizzmole
  • 1,437
  • 18
  • 26
0

To get around the problems explained by eyquem, you could use the copy module to return a tuple containing (reversed) copies of the values, via a function:

from copy import copy

def swapper(x, y):
  return (copy(y), copy(x))

Same function as a lambda:

swapper = lambda x, y: (copy(y), copy(x))

Then, assign those to the desired names, like this:

x, y = swapper(y, x)

NOTE: if you wanted to you could import/use deepcopy instead of copy.

Malekai
  • 4,765
  • 5
  • 25
  • 60
0

That syntax is a standard way to swap variables. However, we need to be careful of the order when dealing with elements that are modified and then used in subsequent storage elements of the swap.

Using arrays with a direct index is fine. For example:

def swap_indexes(A, i1, i2):
      A[i1], A[i2] = A[i2], A[i1]
      print('A[i1]=', A[i1], 'A[i2]=', A[i2])
      return A

  A = [0, 1, 2, 3, 4]
  print('For A=', A)
  print('swap indexes 1, 3:', swap_indexes(A, 1, 3))

Gives us:
('For A=', [0, 1, 2, 3, 4])
('A[i1]=', 3, 'A[i2]=', 1)
('swap indexes 1, 3:', [0, 3, 2, 1, 4])

However, if we change the left first element and use it in the left second element as an index, this causes a bad swap.

def good_swap(P, i2):
    j = P[i2]
    #Below is correct, because P[i2] is modified after it is used in P[P[i2]]
    print('Before: P[i2]=', P[i2], 'P[P[i2]]=', P[j])
    P[P[i2]], P[i2] = P[i2], P[P[i2]]
    print('Good swap: After P[i2]=', P[i2], 'P[P[i2]]=', P[j])
    return P

def bad_swap(P, i2):
    j = P[i2]
    #Below is wrong, because P[i2] is modified and then used in P[P[i2]]
    print('Before: P[i2]=', P[i2], 'P[P[i2]]=', P[j])
    P[i2], P[P[i2]] = P[P[i2]], P[i2]
    print('Bad swap: After P[i2]=', P[i2], 'P[P[i2]]=', P[j])
    return P

P = [1, 2, 3, 4, 5]
print('For P=', P)
print('good swap with index 2:', good_swap(P, 2))
print('------')
P = [1, 2, 3, 4, 5]
print('bad swap with index 2:', bad_swap(P, 2))

('For P=', [1, 2, 3, 4, 5])
('Before: P[i2]=', 3, 'P[P[i2]]=', 4)
('Good swap: After P[i2]=', 4, 'P[P[i2]]=', 3)
('good swap with index 2:', [1, 2, 4, 3, 5])

('Before: P[i2]=', 3, 'P[P[i2]]=', 4)
('Bad swap: After P[i2]=', 4, 'P[P[i2]]=', 4)
('bad swap with index 2:', [1, 2, 4, 4, 3])

The bad swap is incorrect because P[i2] is 3 and we expect P[P[i2]] to be P[3]. However, P[i2] is changed to 4 first, so the subsequent P[P[i2]] becomes P[4], which overwrites the 4th element rather than the 3rd element.

The above scenario is used in permutations. A simpler good swap and bad swap would be:

#good swap:
P[j], j = j, P[j]
#bad swap:
j, P[j] = P[j], j
JayS
  • 2,057
  • 24
  • 16
-5

You can combine tuple and XOR swaps: x, y = x ^ x ^ y, x ^ y ^ y

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

x, y = x ^ x ^ y, x ^ y ^ y

print('After swapping: x = %s, y = %s '%(x,y))

or

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

print('After swapping: x = %s, y = %s '%(x ^ x ^ y, x ^ y ^ y))

Using lambda:

x, y = 10, 20

print('Before swapping: x = %s, y = %s' % (x, y))

swapper = lambda x, y : ((x ^ x ^ y), (x ^ y ^ y))

print('After swapping: x = %s, y = %s ' % swapper(x, y))

Output:

Before swapping: x =  10 , y =  20
After swapping: x =  20 , y =  10
u-betcha
  • 1
  • 2
  • 1
    The xor operator (^) only works on ints, and it's always true that x ^ x = 0 and 0 ^ x = x, hence x ^ x ^ y = y and x ^ y ^ y = x always. That makes this solution equivalent to the simpler x, y = y, x but only working on ints. – David Rubio Mar 02 '23 at 10:21