We launched the course Python Scripting for System Administrators, which touches on both Python 2 and Python 3. This launch is a great time to discuss why this version difference is such a big deal and what you should look out for.

A Brief Aside on Versioning

If you utilize SemVer for versioning a software project then a major version change is the time to make “breaking changes,” and that’s what Python 3 did. From a versioning perspective, this is exactly how we should be versioning our projects.

Mistakes Were Made…

Python 3 exists because Guido saw areas of the language that needed some improvements and the changes couldn’t be made with backward compatibility in mind. As someone working on software, you learn as you go and there’s almost no way to know everything when starting out. It makes a lot of sense that a project’s design would take some turns that are later found to be less than desirable. Python 3 is a great example of paying off “technical debt”, but that decision wasn’t well received initially.

At the point that Python 3 was released, there was a lot of Python code in production, both open-source and proprietary. As you can imagine, it’s hard upgrading to a new version of a language in your business when it requires that you potentially change large areas of your application code. This is the problem that Python 3 faced, so even though Python 3 is great, it’s hard to move such a large community overnight.

Key Differences

Here are the differences between the two major versions that I think are most likely to affect your Python code:

Strings & Binary Data (bytes)

In version 2, you can mix Unicode data (bytes) with 8-bit strings, and it would occasionally work, but not always. This lead to the change in Python 3; you’re no longer able to implicitly mix text and binary data. The area where this is most likely to trip you up will be interacting with file contents depending on the mode of the file. Here’s an example that can demonstrate how the same code will generate different types and errors between Python 2 and 3.

str_vs_bytes.py

f = open("example.txt", 'rb') # opening in 'binary' read mode

data = f.read()
other_str = "other string"

print("data is of type: %s" % type(data))
print("other_str is of type: %s" % type(other_str))

data + other_str # concatenating to see if an error occurs

If we run this script with Python 2 we get the following:

$ python2 str_vs_bytes.py
data is of type: <type 'str'>
other_str is of type: <type 'str'>

Doing the same thing with Python 3, we will see an error, and that other_str has a different type than data:

$ python3 str_vs_bytes.py
data is of type: <class 'bytes'>
other_str is of type: <class 'str'>
Traceback (most recent call last):
  File "str_vs_bytes.py", line 9, in <module>
    data + other_str
TypeError: can't concat str to bytes

The biggest hurdle here is making sure that you are always opening files with the proper mode in Python 3.

One of the most common things done in programming is printing to the screen. Python makes printing about as simple as possible through the use of the print statement in Python 2 and the print function in Python 3. This difference is pretty subtle, but between Python 2 and 3, the use of print changed so that it is a function and thus requires parentheses. This line works perfectly fine in Python 2, but will raise an exception in Python 3:

Python 2

>>> print "something"
something

Python 3

>>> print "something"
  File "<stdin>", line 1
    print "something"
                    ^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print("something")?

Thankfully, the error explains the situation well, and if you add parentheses to your Python 2 print usage, you won’t run into any issues.

Integer Division

In Python 2, the division of 2 integers would always return an integer. This makes sense, in that you might not want to cast from int to float implicitly, but as a programmer when you think of division you just want to do the normal math. This behavior was changed in Python 3 so that standard division would return the full result instead of a truncated value.

Python 2

>>> 1 / 2 == 1 // 2
True
>>> 1 / 2
0

Here’s the same example using Python 3:

>>> 1 / 2 == 1 // 2
False
>>> 1 / 2
0.5

Writing Code for Python 2 & 3

In the course, we utilize Test Driven Development (TDD) to help us design our software, but one of the added benefits we get is that we have a test suite that we can run against multiple versions of Python. This sort of approach makes it a little easier to be confident that you’re writing software that supports both major versions, but if you aren’t writing tests, then it’s important that you know the main differences.

DevOps Guides

Leave a Reply

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

Get actionable training and tech advice

We'll email you our latest articles up to once per week.