Python 3 was born out of necessity. One of Python 2's major annoyances was its inconsistent handling of non-English characters (commonly manifested as the infamous UnicodeDecodeError
exception). Guido initiated the Python 3 project to clean up a number of such language issues while breaking backward compatibility.
The first alpha release of Python 3.0 was made in August 2007. Since then, Python 2 and Python 3 have been in parallel development by the core development team for a number of years. Ultimately, Python 3 is expected to be the future of the language.
This section covers the most important changes in Python 3 from a Django developer's perspective. For the full list of changes, please refer to the recommended reading section at the end of this chapter.
The examples are given in both Python 3 and Python 2. Depending on your installation, all Python 3 commands might need to be changed from python
to python3
or python3.4
.
In Python 3, the __str__()
method is called for string representation of your models rather than the awkward sounding __unicode__()
method. This is one of the most evident ways to identify Python 3 ported code:
Python 2 |
Python 3 |
---|---|
class Person(models.Model): name = models.TextField() def __unicode__(self): return self.name |
class Person(models.Model): name = models.TextField() def __str__(self): return self.name |
The preceding table reflects the difference in the way Python 3 treats strings. In Python 2, the human-readable representation of a class can be returned by __str__()
(bytes) or __unicode__()
(text). However, in Python 3 the readable representation is simply returned by __str__()
(text).
Python 2 has two kinds of classes: old-style (classic) and new-style. New-style classes are classes that directly or indirectly inherit from object
. Only the new-style classes can use Python's advanced features, such as slots, descriptors, and properties. Many of these are used by Django. However, classes were still old-style by default for compatibility reasons.
In Python 3, the old-style classes don't exist anymore. As seen in the following table, even if you don't explicitly mention any parent classes, the object
class will be present as a base. So, all the classes are new-style.
Python 2 |
Python 3 |
---|---|
>>> class CoolMixin: ... pass >>> CoolMixin.__bases__ () |
>>> class CoolMixin: ... pass >>> CoolMixin.__bases__ (<class 'object'>,) |
The simpler call to super()
, without any arguments, will save you some typing in Python 3.
Python 2 |
Python 3 |
---|---|
class CoolMixin(object): def do_it(self): return super(CoolMixin, self).do_it() |
class CoolMixin: def do_it(self): return super().do_it() |
Specifying the class name and instance is optional, thereby making your code DRY and less prone to errors while refactoring.
Imagine the following directory structure for a package named app1
:
/app1 /__init__.py /models.py /tests.py
Now, in Python 3, let's run the following code in the parent directory of app1
:
$ echo "import models" > app1/tests.py $ python -m app1.tests Traceback (most recent call last): ... omitted ... ImportError: No module named 'models' $ echo "from . import models" > app1/tests.py $ python -m app1.tests # Successfully imported
Within a package, you should use explicit relative imports while referring to a sibling module. You can omit __init__.py
in Python 3, though it is commonly used to identify a package.
In Python 2, you can use import models
to successfully import the models.py
module. However, it is ambiguous and can accidentally import any other models.py
in your Python path. Hence, this is forbidden in Python 3 and discouraged in Python 2 as well.
In Python 3, according to PEP 3333 (amendments to the WSGI standard), we are careful not to mix data coming from or leaving via HTTP, which will be in bytes, as opposed to the text within the framework, which will be native (Unicode) strings.
Essentially, for the HttpRequest
and HttpResponse
objects:
str
objectsbyte
objectsUnlike Python 2, the strings and bytes are not implicitly converted while performing comparisons or concatenations with each other. Strings mean Unicode strings only.
Exception-handling syntax and functionality has been significantly improved in Python 3.
In Python 3, you cannot use the comma-separated syntax for the except
clause. Use the as
keyword instead:
Python 2 |
Python 3 and 2 |
---|---|
try: pass except e, BaseException: pass |
try: pass except e as BaseException: pass |
The new syntax is recommended for Python 2 as well.
In Python 3, all the exceptions must be derived (directly or indirectly) from BaseException
. In practice, you would create your custom exceptions by deriving from the Exception
class.
As a major improvement in error reporting, if an exception occurs while handling an exception, then the entire chain of exceptions are reported:
Python 2 |
Python 3 |
---|---|
>>> try: ... print(undefined) ... except Exception: ... print(oops) ... Traceback (most recent call last): File "<stdin>", line 4, in <module> NameError: name 'oops' is not defined |
>>> try: ... print(undefined) ... except Exception: ... print(oops) ... Traceback (most recent call last): File "<stdin>", line 2, in <module> NameError: name 'undefined' is not defined During the handling of the preceding exception, another exception occurred: Traceback (most recent call last): File "<stdin>", line 4, in <module> NameError: name 'oops' is not defined |
Once you get used to this feature, you will definitely miss it in Python 2.
The core developers have cleaned up and organized the Python standard library. For instance, SimpleHTTPServer
now lives in the http.server
module:
Python 2 |
Python 3 |
---|---|
$ python -m SimpleHTTP ServerServing HTTP on 0.0.0.0 port 8000 ... |
$python -m http.server Serving HTTP on 0.0.0.0 port 8000 ... |
Python 3 is not just about language fixes. It is also where bleeding-edge Python development happens. This means improvements to the language in terms of syntax, performance, and built-in functionality.
Some of the notable new modules added to Python 3 are as follows:
Even if some of these modules have backports to Python 2, it is more appealing to migrate to Python 3 and leverage them as built-in modules.
Most serious Python developers prefer to use virtual environments. virtualenv
is quite popular for isolating your project setup from the system-wide Python installation. Thankfully, Python 3.3 is integrated with a similar functionality using the venv
module.
Since Python 3.4, a fresh virtual environment will be pre-installed with pip, a popular installer:
$ python -m venv djenv [djenv] $ source djenv/bin/activate [djenv] $ pip install django
Notice that the command prompt changes to indicate that your virtual environment has been activated.
We cannot possibly fit all the Python 3 changes and improvements in this appendix. However, the other commonly cited changes are as follows:
print()
is now a function: Previously, it was a statement, that is, arguments were not in parenthesis.sys.maxint
is outdated, integers will have unlimited precision. <>
is removed: Use !=
instead.3 / 2
would evaluate to 1
. It will be correctly evaluated to 1.5
in Python 3.range
instead of xrange(): range()
will now return iterators as xrange()
used to work before.dict
and dict
-like classes (such as QueryDict
) will return iterators instead of lists for the keys()
, items()
, and values()
method calls.