allenfrostline

Pythonic Swapping: How and Why


2018-07-12

It’s not hard to write a swap function. The most orthodox way that’s being used in C++ or Java is by using a temporary variable. For example, say we have a = 0 and b = 1, and we’d like to swap the values of these two variables. The pseudocode shall be something as below.

temp = a
a = b
b = temp

However, a more “Pythonic” way to do so is by literally “swapping” the values in place. Specifically, we don’t even need to define a function for it, so the title picture is actually nonsense.

a, b = b, a

How is that handled inside Python? Before answering that question, how is “Pythonic” defined? Well, Pythonic means code that doesn’t just get the syntax right but that follows the conventions of the Python community and uses the language in the way it is intended to be used (Abien Fred Agarap1). Talking about the conventions of the Python community, we won’t be able to miss the famous Zen of Python:

import this
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

So our one-line swapping exactly follows these supreme principles: it’s beautiful, explicit, simple and perfectly readable. The only remaining question is, what happened when we called a, b = b, a, and what are the technical differences between this lazy trick and the orthodox one?

Well, here is the thing. Just like most other programming languages, Python also handles assignment statements in a right-to-left manner. So before we actually assign the value of a to b and vice versa, Python pakcages the RHS as a tuple temporarily stored in memory. Then it assigns the values of this tuple to the LHS in order. That it. As a result, different from the orthodox swap function which creates a temporary variable temp staying in our memory until being collected manually (if we’re using it in the global environment) or after the function is destroyed, the Pythonic swap occupies doubled memory yet frees automatically thanks to Python’s garbage collection. That’s kind of a tradeoff and in some cases when absolute available memory is critically short, we might be suggested to use the more orthodox swap function.

Just as a supplement, there is in fact a way to swap in place while avoid using doubled memory. The trick is illustrated as follows.

a = a + b
b = a - b
a = a - b

In case of large integers, we may also use XOR functions:

a = a ^ b
b = a ^ b
a = a ^ b

  1. https://stackoverflow.com/questions/25011078/what-does-pythonic-mean ↩︎