48

Since Django doesn't yet support Python 3.x, I'm using Python 2.7. However, I'd like to go ahead and start familiarizing myself with the new Python 3.x syntax as much as possible. Which leads me to the question:

  • What is the best way to write Python 2.7 code that will be as compatible as possible with Python 3.x?

I know that running python -3 will

Warn about Python 3.x incompatibilities that 2to3 cannot trivially fix.

However, I'm interested in getting used to Python 3.x syntax while still using Python 2.7.

For instance, it seems that I should be using the following imports to my code:

from __future__ import print_function
from __future__ import unicode_literals
from __future__ import division
from __future__ import absolute_import

The above four __future__ import statements are required as of Python 3.0, but not required in 2.7 as described in Python 2.7.3's documentation 27.11. Future Statement Definitions

What else?

Matthew Rankin
  • 457,139
  • 39
  • 126
  • 163
  • 1
    One downside of this approach is if your program has dependencies on packages incompatible with one the __future __ utility, your program will fail and you don't have much control. For example you use one of simple pypi package and it has "print something". Thanks anyways it's good question. – Shekhar May 19 '11 at 08:40
  • 17
    @Shekhar: the effect of `__future__` is localized to a module that imports it. There is no problem with a print statement; only modules that have `from __future__ import print_function` at the top can use the `print()` function. – jfs Dec 10 '11 at 22:10

7 Answers7

19

Many modules these days get rewritten in a way that allows execution on both Python 2 and Python 3. This turns out to be not very hard at all, and in the future it will be very easy to just drop Python 2 support.

Take a look at the six module that helps with this task, encapsulating many of the differences in a convenient way:

Six provides simple utilities for wrapping over differences between Python 2 and Python 3.

Its website (and of course, code) lists a lot of ways to make this possible.

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
10

Put the following code into a py3k.py module and import it like this: from py3k import *. You need to put it in every file though, but you can even leave it there if nobody uses Python 2.x anymore or you could just search & replace the import line with whitespace and then remove the file.

try:
    from future_builtins import *
except ImportError:
    pass

try:
    input = raw_input
    range = xrange
except NameError:
    pass

And this is how my template file looks:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""

"""

from __future__ import division, absolute_import, \
                       print_function, unicode_literals
from utils.py3k import *  # @UnusedWildImport


#
Joschua
  • 5,816
  • 5
  • 33
  • 44
8

Many Python IDE's can be of big help here.

PyCharm, for example, can be configured to check for compatibility with any range of versions,

enter image description here

and report issues at any level of severity:

enter image description here

orome
  • 45,163
  • 57
  • 202
  • 418
  • Ninja IDE (http://ninja-ide.org/) also has a similar module to help you convert your code to 3.X. – Gabriel Mar 05 '14 at 01:53
8

You also need to use the new exception syntaxes, ie no more

try:
     raise Exception, "Message"
except Exception, e:
     pass

instead you should do:

try:
     raise Exception("Message")
except Exception as e:
     pass

Also make sure you prefix all your binary strings with a b, ie:

b'This is a binary string'

For a more complete cover of this topic, see http://python3porting.com/noconv.html

Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251
6
try:
    input = raw_input
    range = xrange
except NameError:
    pass

Are two ones that spring to mind...

Jakob Bowyer
  • 33,878
  • 8
  • 76
  • 91
4

I propose you to give a try for future library. From their site:

python-future is the missing compatibility layer between Python 2 and Python 3. It allows you to use a single, clean Python 3.x-compatible codebase to support both Python 2 and Python 3 with minimal overhead.

It provides future and past packages with backports and forward ports of features from Python 3 and 2. It also comes with futurize and pasteurize, customized 2to3-based scripts that helps you to convert either Py2 or Py3 code easily to support both Python 2 and 3 in a single clean Py3-style codebase, module by module.

Notable projects that use python-future for Python 2/3 compatibility are Mezzanine and ObsPy.

Community
  • 1
  • 1
rominf
  • 2,719
  • 3
  • 21
  • 39
1

Avoing range() and zip(), using xrange() and itertools.izip() instead.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • 5
    The problem with using `xrange` is that it's missing in Python 3, so the same code has no chance to run there. If one is willing to give up the possible performance benefits of `xrange`, it's better to use `range` instead, making code more python 2 *and* 3 compatible – Eli Bendersky May 09 '11 at 13:11
  • @Eli Why not do (like my example) a try except to make range = xrange? – Jakob Bowyer May 09 '11 at 13:14
  • 3
    @Eli: But by using xrange etc you can then trivially run 2to3 on the code, while otherwise you might do things like `zip(foo)[2]`, which won't work since the result in Python 3 is not a list. – Lennart Regebro May 09 '11 at 15:19
  • 2
    @Lennart: agreed about 2to3. It all really depends on the approach one takes to implement the transition. – Eli Bendersky May 09 '11 at 15:25