Migrating from Python 2 to Python 3

Modern Python offers powerful features such as asynchronous programming (using async and await) and memory-efficient data processing with generators and iterators. Leveraging these advancements, primarily found in Python 3, can significantly improve your code’s performance and maintainability. This often necessitates migrating existing Python 2 projects. The migration process can vary from a straightforward conversion to a substantial refactoring. To help you navigate this, we’ll discuss some of the crucial differences between the two Python versions.

1. PRINT:

This one is easy – most of the time it’s just about translating code from:

print "abcd";

to:

print("abcd")

2. DICT KEYS() AND ITEMS():

This works in Python 2:

x = {1:2, 3:4, 5:6}
k = x.keys()
z = k[0:2]
# [1, 3]

Unfortunately, the return type for keys() and items() is different and non-subscriptable in Python 3; this means k[0:2] would fail. A quick, suboptimal workaround for the code above could just be:

x = {1:2, 3:4, 5:6}
k = list(x.keys())
z = k[0:2]
# [1, 3]

3. RANGE() AND XRANGE():

The xrange() construct is used in Python 2 as an infinite generator, that is in for loops, while range() returns lists:

a = [i for i in xrange(23)]
# [0, 1, ... , 22]
b = range(23)
# [0, 1, ... , 22]

In Python 3 range() is a replacement for xrange(), while the range() implementation found in Python 2 is now gone, but it could probably be replaced with a list comprehension.

4. EXCEPTIONS:

Python 2 code with exceptions looks messy:

try:
	raise IOError, "file error";
except IOError, err:
	print err

Python 3 is cleaner:

try:
	raise IOError("file error")
except IOError as err:
	print(err)

5. ROUNDING AND DIVISION:

These changes may break programs in very non intuitive ways:

a = round(122.5)
b = 7/5

# Python 2:
# a = 123, b = 1

# Python 3:
# a = 122, b = 1.4

Long story short, Python 2 considers the types and then does an integer or a floating point division depending on what it has found. Python 3 is more predictable from this point of view – it always does a floating point division.

Rounding is even harder to make sense of, because some Python 2 versions round numbers down, while others round them up. Again, Python 3 has brought more clarity by rounding up or down to the nearest even number.


There are far more differences – if your code uses (let’s say) urllib, then all call points need to be adjusted for a new syntax. To end on a higher note, I have to mention this automated tool that may be able to deal with this tedious migration task: 2to3.


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.