Tobias Kienzler
Tobias Kienzler

Reputation: 27403

Can a from __future__ import ... guarantee Python 2 and 3 compatibility?

I'm not interested in warming up the "Python 2 or Python 3?" questions (even though the most recent one I found is over one year old), but I stumbled upon this claim:

You can write the Python 3 code under Python 2 if your file begins with the line:

from __future__ import absolute_import, division, generators, unicode_literals, print_function, nested_scopes, with_statement

With that line in place, your code will work with either Python 2 or Python 3. There may be rare cases in which it doesn't work, but I have not found any,

Is this true? Is this single line enough to make sure the code you write will run on both Python 2.x (>=2.5 I assume) and 3.x (assuming the modules imported are available in both)?

Upvotes: 12

Views: 5494

Answers (6)

doep
doep

Reputation: 449

You can use the future package: pip install future

In [1]: range(10)
Out[1]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [2]: from future.builtins import range

In [3]: range(10)
Out[3]: range(0, 10)

Here is their cheat sheet with more examples: http://python-future.org/compatible_idioms.html

Upvotes: 1

kindall
kindall

Reputation: 184211

I would say that no, this is baloney. Even with those imports, there are still significant differences between Python 2 and 3: for example, input() in Python 3 is like raw_input() in Python 2; range() in Python 3 is like xrange() in Python 2. In the case of xrange() you could probably get away with using range() in Python 2 as long as the ranges are small, but if they're large, your program could have very different memory usage under Python 2 and Python 3.

You could add something like this to your code:

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

But then you've got to find all those edge cases and fix them up. For example, there are the keys() and values() methods of dict that return iterators in Python 3 but lists in Python 2, so you'd need to write a dict subclass that "fixes" that (and then never use dictionary literals in your code without wrapping them, since those would otherwise be of the built-in dict type).

I suppose that, by using __future__ and various fix-ups, and by limiting yourself to writing code in a subset of Python thus created that will run under both 2.x and 3.x, it might be possible to write code that runs in both versions. Seems like a lot of work, though. There's a reason there's a 2to3 utility...

Upvotes: 8

Lennart Regebro
Lennart Regebro

Reputation: 172249

"It depends"

No: Adding these imports to your Python 2 code will not make it run under Python 3.

Yes: With these imports in place you can write code that runs under both Python 2 and Python 3.

But: Then again, you can do that without those imports as well, and several of them, such as unicode_literals have turned out to simply not be helpful. generators and with_statement have nothing to do with Python 2 to Python 3 at all, those are features added in versions of Python 2.

So in conclusion, these imports are a bit of a red herring, and the statement is more wrong than right.

However, that doesn't mean writing code that runs under both Python 2 and Python 3 is impossible, or even necessarily very hard. See http://python3porting.com/ for more info.

Upvotes: 7

cdarke
cdarke

Reputation: 44364

Nah. Others have pointed out some difference, there are others. One of the most fundamental is that Python 3 native strings are multibyte - this raises issues when communicating with single-byte mechanisms, like pipes to other processes. Others include the renaming of modules (Tkinter to tkinter), True and False are now keywords.

Even comparisons might not be the same, the following incorrect code:

num = 42
txt = "3"

if txt < num:
    print ('Wow!')
else:
    print ('Doh!')

produces a TypeError on Py3, but not on Py2.

Unpacking has been mentioned. the dictionary methods items(), keys(), and values() return view objects (different method names are used on 2.7). In Py3 iterators are used more, for example returned from map() and filter(), and so on....

Upvotes: 3

ecatmur
ecatmur

Reputation: 157364

It's not impossible, depending on the demands of your codebase. You will probably find the six (2 * 3, haha) library essential; another useful tool is python-modernize which attempts to convert your code to a cross-compatible state.

Upvotes: 5

Gareth Latty
Gareth Latty

Reputation: 89017

It will make it more likely, but there are some things that cannot be gained from a __future__ import, and some things that are removed going into 3.x.

Off the top of my head, you could still use parameter tuple unpacking, which is removed in 3.x, and you won't be able to use the nice tuple unpacking with the star operator that is introduced in 3.x.

E.g:

def some_function((x, y), magnitude): #This has been removed in 3.x
    pass

x, *y = (1, 2, 3) #This does not exist in 2.x

That said, with some care to avoid such things, you could definitely write code that works across both versions, yes.

Obviously, this also only applies to 2.x versions that have the features backported into them. Because of this, some of that line is actually completely pointless - for example, there is no reason to import generators as any version that can import the with statement will already have generators working as standard. Same goes for nested_scopes.

In general, I recommend just writing for 3.x - there are no barriers to having both versions installed and usable. If you really desperately need 2.x support, then write for 2.x with as many of the back-ported features as you want, and use 2to3 to clean up anything else.

Upvotes: 4

Related Questions