$$ \newcommand{\tp}{\thinspace .} $$

This chapter is taken from the book A Primer on Scientific Programming with Python by H. P. Langtangen, 5th edition, Springer, 2016.

Making code that is compatible with Python 2 and 3

Python 2 and 3 are very similar, but there are some basic differences that make a program written in one version incompatible with the other version. The most important differences are that 1/10 is interpreted as float division (equal 0.1) in Python 3, while Python 2 interprets the expression as integer division (which equals 0). The raw_input function in Python 2 is named input in Python 3, while the input function in Python 2 (meaning eval applied to raw_input) does not exist in Python 3. The major difference, though, is the print statement in Python 2, which must be a call to the print function in Python 3. Some more differences related to constructions in this document appear below.

Basic differences between Python 2 and 3

xrange in Python 2 is range in Python 3

The range function in Python 2 generates a list of integers, and for very long loops this list may consume significant computer memory. The xrange function in Python was therefore made to just generate a series of integers without storing them. In Python 3, range is the xrange function from Python 2. If one wants a list of integers in Python 3, one has to do list(range(5)) to store the output from range in a list.

Python 3 often avoids returning lists and dictionaries

The Python 3 idea of letting range just generate one object at a time instead of storing all of them applies to many other constructions too. Let d be a dictionary. In Python 2, d.keys() returns a list of the keys in the dictionary, while in Python 3, d.keys() just enables iteration over the keys in a for loop. Similarly, d.values() and d.items() returns lists of values or key-value pairs in Python 2, while in Python 3 we can only iterate over the values in a for loop. A simple loop like

for key in d.keys():
    ...
works well for both Python versions, but

keys = d.keys()
in Python 2, where we want the keys as a list, needs a modification in Python 3:

keys = list(d.keys())
We should add that for key in d.keys() is not the preferred syntax anyway - use for key in d. Also, if we just want a for loop over all key-value pairs, we can use d.iteritems() which does not return any list, neither in Python 2 nor in Python 3.

Library modules have different names

We have used the urllib module in the sections How to access web pages in programs and Example: Reading pure text files. Python 3 has some different names for this module:

# Python 2
import urllib
with urllib.urlopen('http://google.com') as webfile:
    text = webfile.read()
urllib.urlretrieve('http://google.com', filename='tmp.html')

# Python 3
import urllib.request as urllibr
with urllibr.urlopen('http://google.com') as webfile:
    text = webfile.read()
urllibr.urlretrieve('http://google.com', filename='tmp.html')
A lot of other modules have also changed names, but the futurize program (see below) help you to find the right new names.

Python 3 has unicode and byte strings

A standard Python 2 string, s = 'abc', is a sequence of bytes, called byte string in Python 3, declared as s = b'abc' in Python 3. The assignment s = 'abc' in Python 3 leads to a unicode string and is equivalent to s = u'abc' in Python 2. To convert a byte string in Python 3 to an ordinary (unicode) string, do s.decode('utf-8'). String handling is often the most tricky task when converting Python 2 code to Python 3.

Python 3 has different relative import syntax inside packages

If you work with Python packages, relative imports inside a package has slightly different syntax in Python 2 and 3. Say you want to import somefunc from a module somemod in some other module at the same level (same subfolder) in the package. Python 2 syntax would be from somemod import somefunc, while Python 3 demands from .somemod import somefunc. The leading dot in the module name indicates that somemod is a module located in the same subfolder as the file containing this import statement. The alternative import, import somemod, in Python 2 must read from . import somemod in Python 3.

Turning Python 2 code into Python 3 code

One can use the futurize program to turn a Python 2 program into a version that works with both Python 2 and 3. The recommended command for turning a program or module prog.py of arbitrary complexity common Python 2/3 version is

Terminal> futurize --all-imports -w -n -o py23 prog.py
The file py23/prog.py will usually run under Python 2 and 3, but sometimes manual adjustments are needed.

By frequently running just futurize prog.py to see what needs to be changed, you can learn a lot of the differences between Python 2 and 3 and also change your programming style in Python 2 so that it comes even closer to Python 3. The python-future documentation has a very useful list of difference between Python 2 and 3 and recipes on how to make common code for both versions.

Porting of larger programs from Python 2 to 3 is recommended to use futurize in a two-stage fashion.