Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to write Python 2 and 3 compatible code using nothing but the standard library

I am trying to make some of my code Python 2 and 3 compatible.

At the moment I am struggling with functions like range/xrange and methods like dict.items/dict.iteritems. Ideally I would like my code to be able to use the former in Python 3.x and the latter in Python 2.x.

Using if/else seems to me to be the easiest way to implement this:

if py >= 3:
    for item in array.items()
    ...
else:
    for item in array.iteritems()

However, doing like that results in lots of repeated and ugly code. Is there a better way to do that using only the standard library? Can I just state somewhere at the beginning of the code to always use range/dict.items if py >= 3 and xrange/dict.iteritems if not?

Is it possible to do something like this?

if py < 3:
    use xrange as range

I have looked around and I know that several libraries, like six o futurize) are used to solve this issue. However I am working on a server that run only python 2.7 and I am not allowed to install any extra libraries on it. I have some python3 code I would like to use but I also want to maintain only one version of the code.

like image 945
alec_djinn Avatar asked May 24 '15 10:05

alec_djinn


People also ask

Are python 2 and 3 compatible with each other?

The latest stable version is Python 3.9 which was released in 2020. The nature of python 3 is that the changes made in python 3 make it incompatible with python 2. So it is backward incompatible and code written in python 3 will not work on python 2 without modifications.

Is code written in python 3 is backward compatible with python 2?

Python 3 is not backwards compatible with Python 2, so your code may need to be adapted. Please start migrating your existing your existing Python 2 code to Python 3. Python 2 series End Of Life is set to 1st of January 2020.

How do I know if python 3 is compatible?

Just open the python files in the pycharm editor, it will show warnings if the code is not compatible to Python2 or Python3. Here is the screenshot where it shows print command syntax warning.


2 Answers

The simple, "Don't Make Me Think!" solution I use is to start simple scripts with:

#!/usr/bin/env python
# just make sure that Python 3 code runs fine with 2.7+ too ~98% of the time :)
from __future__ import (division, print_function, absolute_import,
                        unicode_literals)
from builtins import int
try:
    from future_builtins import ascii, filter, hex, map, oct, zip
except:
    pass
import sys
if sys.version_info.major > 2:
    xrange = range

(Extra tip to stop most pep8 linters for unnecessarily yelling at you for this: move last 3 lines inside and at the top of the try block above)

But the only case I use this is basically "shell scripts that were too large and hairy so I quickly rewrote them to Python and I just want them to run under both Python 2 and 3 with 0 dependencies". Please do NOT use this in real application/library code until you know exactly what are the consequences of all the lines above, and if they are enough for your use case.

Also, the "solution" in this case for .iteritems is "just don't use it", ignore memory use optimizations and just always use .items instead - if this matters, it means you're not writing a "0 dependencies simple script" anymore, so just pick Python 3 and code for it (or Python 2 if you need to pretend we're in 2008).

Also, check these resources to get a proper understanding:

  • http://python-future.org/compatible_idioms.html
  • http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/
  • https://wiki.python.org/moin/PortingToPy3k/BilingualQuickRef

(NOTE: I'm answering this already answered question mainly because the accepted answers roughly translates to "you are stupid and this is dumb" and I find this very rude for an SO answer: no matter how dumb the question, and how "wrong" to actually answer it, a question deserves a real answer._

like image 174
NeuronQ Avatar answered Sep 19 '22 05:09

NeuronQ


import sys

if sys.version_info.major > 2:
    xrange = range

But as Wim implies, this is basically rewriting six yourself.

And as you can see, six does a lot more that handling range. Just e.g. look at the _moved_attributes list in the six source code.

And while Python comes with "batteries included", its standard library is not and cannot be all-encompassing. Nor is it devoid of flaws.

Sometimes there are better batteries out there, and it would be a waste not to use them. Just compare urllib2 with requests. The latter is much nicer to work with.

like image 26
Roland Smith Avatar answered Sep 21 '22 05:09

Roland Smith