Posted on

The key differences between Python 2.x and Python 3.x with examples

Free-clip-art-thinkingMany novice Python users are wondering with which version of Python they should start. My answer to this question is usually something along the lines “just go with the version your favourite tutorial was written in, and check out the differences later on.”

But what if you are starting a new project and have the choice to pick? I would say there is currently no “right” or “wrong” as long as both Python 2.7.x and Python 3.x support the libraries that you are planning to use. However, it is worthwhile to have a look at the major differences between those two most popular versions of Python to avoid common pitfalls when writing the code for either one of them, or if you are planning to port your project. After looking at the differences if you are still not able to decide then this post might help.

What are the differences?

Python 3.0 was released in 2008. The final 2.x version 2.7 release came out in mid-2010, with a statement of extended support for this end-of-life release. The 2.x branch will see no new major releases after that. 3.x is under active development and has already seen over five years of stable releases, including version 3.3 in 2012, 3.4 in 2014, 3.5 in 2015, and 3.6 in 2016. This means that all recent standard library improvements, for example, are only available by default in Python 3.x.

Guido van Rossum (the original creator of the Python language) decided to clean up Python 2.x properly, with less regard for backwards compatibility than in the case for new releases in the 2.x range. The most drastic improvement is the better Unicode support (with all text strings being Unicode by default) as well as saner bytes/Unicode separation.

Besides, several aspects of the core language (such as print and exec being statements, integers using floor division) have been adjusted to be easier for newcomers to learn and to be more consistent with the rest of the language, and old cruft has been removed (for example, all classes are now new-style, “range()” returns a memory efficient iterable, not a list as in 2.x).

The What’s New in Python 3.0 document provides a good overview of the major language changes and likely sources of incompatibility with existing Python 2.x code. Nick Coghlan (one of the CPython core developers) has also created a relatively extensive FAQ regarding the transition.

However, the broader Python ecosystem has amassed a significant amount of quality software over the years. The downside of breaking backwards compatibility in 3.x is that some of that software (especially in-house software in companies) still doesn’t work on 3.x yet.

Some syntax differences :-

Division operator

If we are porting our code or executing the python 3.x code in python 2.x, it can be dangerous if integer division changes go unnoticed (since it doesn’t raise any error). It is preferred to use the floating value (like 7.0/5 or 7/5.0) to get the expected result when porting our code.

print 7 / 5
print -7 / 5   
 
'''
Output in Python 2.x
1
-2
Output in Python 3.x :
1.4
-1.4
 
'''

print function
This is the most well known change. In this the print function in Python 2.x is replaced by print() function in Python 3.x,i.e, to print in Python 3.x an extra pair of parenthesis is required.

print 'Hello, Geeks'      # Python 3.x doesn't support
print('Hope You like these facts')
 
'''
Output in Python 2.x :
Hello, Geeks
Hope You like these facts
 
Output in Python 3.x :
File "a.py", line 1
    print 'Hello, Geeks'
                       ^
SyntaxError: invalid syntax
 
'''

As we can see, if we use parenthesis in python 2.x then there is no issue but if we don’t use parenthesis in python 3.x, we get SyntaxError.

Unicode
In Python 2, implicit str type is ASCII. But in Python 3.x implicit str type is Unicode.

print(type('default string '))
print(type(b'string with b '))
 
'''
Output in Python 2.x (Bytes is same as str)
<type 'str'>
<type 'str'>
 
Output in Python 3.x (Bytes and str are different)
<class 'str'>
<class 'bytes'>
'''

Python 2.x also supports Unicode

print(type('default string '))
print(type(u'string with b '))
 
'''
Output in Python 2.x (Unicode and str are different)
<type 'str'>
<type 'unicode'>
 
Output in Python 3.x (Unicode and str are same)
<class 'str'>
<class 'str'>
'''

xrange
xrange() of Python 2.x doesn’t exist in Python 3.x. In Python 2.x, range returns a list i.e. range(3) returns [0, 1, 2] while xrange returns a xrange object i. e., xrange(3) returns iterator object which work similar to Java iterator and generates number when needed.
If we need to iterate over the same sequence multiple times, we prefer range() as range provides a static list. xrange() reconstructs the sequence every time. xrange() doesn’t support slices and other list methods. The advantage of xrange() is, it saves memory when task is to iterate over a large range.

In Python 3.x, the range function now does what xrange does in Python 2.x, so to keep our code portable, we might want to stick to using range instead. So Python 3.x’s range function is xrange from Python 2.x.

for x in xrange(1, 5):
    print(x),
 
for x in range(1, 5):
    print(x),
 
'''
Output in Python 2.x
1 2 3 4 1 2 3 4
 
Output in Python 3.x
NameError: name 'xrange' is not defined
'''

Error Handling

try:
    trying_to_check_error
except NameError, err:
    print err, 'Error Caused'   # Would not work in Python 3.x
 
'''
Output in Python 2.x:
name 'trying_to_check_error' is not defined Error Caused
 
Output in Python 3.x :
File "a.py", line 3
    except NameError, err:
                    ^
SyntaxError: invalid syntax
'''
try:
     trying_to_check_error
except NameError as err: # 'as' is needed in Python 3.x
     print (err, 'Error Caused')
 
'''
Output in Python 2.x:
(NameError("name 'trying_to_check_error' is not defined",), 'Error Caused')
 
Output in Python 3.x :
name 'trying_to_check_error' is not defined Error Caused
'''

_future_module
This is basically not a difference between two version, but useful thing to mention here. The idea of __future__ module is to help in migration. We can use Python 3.x
If we are planning Python 3.x support in our 2.x code,we can ise_future_ imports it in our code.

For example, in below Python 2.x code, we use Python 3.x’s integer division behavior using __future__ module

# In below python 2.x code, division works
# same as Python 3.x because we use  __future__
from __future__ import division
 
print 7 / 5
print -7 / 5
''' output
1.4
-1.4
'''