Python 3 Advantages

Let’s explore the advantages Python 3 has to offer over Python 2!

Print is no longer a statement but a built-in function

Some of the advantages of this change in Python 3:

  • There’s really no reason for print to be a statement. It’s more consistent if print is a function.
  • Since print is a function, it can be passed as an argument to functions that expect a function. For example, take a function that requires another function to further process your data as an argument. For simple mocking/debugging, you can now also pass the print() function.
  • You can use print like this now because it is a function: [print(x) for x in range(10)]
  • You can override the print function by assigning to builtins.print, whereas you can’t do that with a statement.

Unicode in Python 3

Another big Python 3 advantage is that every string, by default, is a Unicode string. In Python 2, a string defaults to an ASCII string, limiting the range of characters it can handle. If you wanted a Unicode string, you had to create one like this explicitly:

# Python 2
unicode_sting = u'Ümlaut? Nō prōblem!'

# Python 3
unicode_sting = 'Ümlaut? Nō prōblem!'

This is a must-have for many countries.

Data classes

Since version 3.7, which is fairly recent, Python offers data classes. There are several advantages over regular classes or other alternatives, like returning multiple values or dictionaries:

  • A data class requires a minimal amount of code.
  • You can compare data classes because __eq__ is implemented for you.
  • You can easily print a data class for debugging because __repr__ is implemented as well.
  • Data classes require type hints, reducing the chances of bugs.

Here’s an example of a data class at work:

from dataclasses import dataclass

@dataclass
class Card:
    rank: str
    suit: str

card = Card("Q", "hearts")

print(card == card)
# True

print(card.rank)
# 'Q'

print(card)
Card(rank='Q', suit='hearts')

Merging dictionaries (Python 3.5+)

Since Python 3.5, it became easier to merge dictionaries:

dict1 = { 'a': 1, 'b': 2 }
dict2 = { 'b': 3, 'c': 4 }
merged = { **dict1, **dict2 }
print (merged)
# {'a': 1, 'b': 3, 'c': 4}

If there are overlapping keys, the keys from the first dictionary will be overwritten.

Divisions became more predictable

In Python 2, the division operator / defaults to an integer division unless one of the operands is a floating-point number. So you have this behavior:

# Python 2
5 / 2 = 2
5 / 2.0 = 2.5

In Python 3, the division operator defaults to a floating-point division and the // operator became an integer division. So we get:

# Python 3
5 / 2 = 2.5
5 // 2 = 2

For the complete motivation behind this change, you should read PEP-0238.

Meaningful comparisons

In Python 2, you could compare anything to everything. The following example would all return True:

"a string" > 2
None < 5

It makes no sense and can hide bugs. In Python 3, these comparisons will throw a TypeError exception.

No more range vs. xrange

Python 2 had two range functions: range and xrange. The latter was faster since it was based on iterators. In Python 3, range has become xrange and the xrange name was dropped. It’s one of the examples where Python became less confusing for newcomers.

Please help us and share this article

About the author

Erik is the owner of Python Land and the author of many of the articles and tutorials on this website. He's been working as a professional software developer for 25 years, and he holds a Master of Science degree in computer science. His favorite language of choice: Python!