Write several examples of code that is compatible with both python2.x and python3.x versions

  • 2020-04-02 14:44:26
  • OfStack

Write code compatible with python2.x and 3.x

While we're in the transition from Python 2.x to Python 3.x, you might wonder if you can run both Python 2 and 3 without changing any code. That seems like a reasonable appeal, but how to get started? What Python 2 code is prone to errors when the 3.x interpreter executes?
Print vs the print ()

If you're thinking like me, you might say print statement, which is a good place to start, just to show you, print is a statement in 2.x, and it's a keyword or a reserved word in 3.x. In other words, because this change involves the syntax of the language, you can't use it in an if statement, and Python still doesn't have the #ifdef macro. Now try to print out the parameters in the brackets:


>>> print('Hello World!')
Hello World!

Cool, this works in both Python2 and Python3, and it works the same way, so take a look at this:
 


>>> print(10, 20) # Python 2
(10, 20)

At this point, you're not as lucky as you were to get the same result. Python2 prints a tuple, while Python3 prints two values when multiple arguments are passed into print() :
 


>>> print(10, 20) # Python 3
10 20

If you're thinking more, we can check to see if print is a keyword. The keyword module contains a list of keywords. Print is not a keyword in 3.x, so it can be verified simply:
 


>>> import keyword
>>> 'print' in keyword.kwlist
False

As a smart programmer, you might expect True when you try 2.x, and while there's nothing wrong with that, you'll still fail for other reasons in order to achieve Python3.
 


>>> import keyword
>>> if 'print' in keyword.kwlist:
...  from __future__ import print_function
...
File "", line 2
SyntaxError: from __future__ imports must occur at the beginning of the file

One solution is to use a function similar to print, one of which is sys.stdout.write() and the other is distutils.log.warn(). For whatever reason, we decided to use the latter. The "hello world" example looks like this:
 


# Python 2.x
print 'Hello World!'
# Python 3.x
print('Hello World!')

The following code can be used in both versions:
 


# Python 2.x & 3.x compatible
from distutils.log import warn as printf
printf('Hello World!')

Why don't we use sys.stdout.write(), because we need to add a NEWLINE character at the end of the string to be compatible with this behavior (the write method in python2.x does not wrap) :
 


# Python 2.x & 3.x compatible
import sys
sys.stdout.write('Hello World!n')

Import your way to a solution

In general, import is fine, as long as the import is correct, but in the following code, we want to import the urlopen() function, in Python2, it exists in both urllib2 and urllib2 (we use the latter), in Python3, it is integrated into urllib.request, and your solution is to be able to work in both 2.x and 3.x:


try:
 from urllib2 import urlopen
except ImportError:
 from urllib.request import urlopen

For memory protection, you might be more interested in the zip() version of iterator(Python3), which in Python2 is itertools.izip(). This function was renamed in Python3 and replaced with zip(). If you use the iterative version, the import statement is also very straightforward:
 


try:
 from itertools import izip as zip
except ImportError:
 pass

Another example is that it seems to not so elegant class instead of in Python2, pure Python version is instead module, means that access is through instead. Instead, as well as a more rapid C language version, is located in the cStringIO. Instead, but this depends on your Python installation version, you can use the cStringIO priority and is instead (if the cStringIO cannot use). In Python3, Unicode is the default type of string, but if you do anything with the network, chances are you'll have to use ASCII/ byte strings, so instead of StringIO, you need io.bytesio, and in order to get what you want, this import looks a little ugly:
 


try:
 from io import BytesIO as StringIO
except ImportError:
 try:
  from cStringIO import StringIO
 except ImportError:
  from StringIO import StringIO

Putting it all together

If you're lucky, that's all you need to be prepared to do, and the rest of the code is simpler than where you started. If you import distutils.log.warn () [printf ()], url * urlopen (), *.stringio, and a standard import: xml.etree.elementtree (2.5 and up), you can now write a very short parser to show the top story from the Google News service in eight lines of code:
 


g = urlopen('http://news.google.com/news?topic=h&output=rss')
f = StringIO(g.read())
g.close()
tree = xml.etree.ElementTree.parse(f)
f.close()
for elmt in tree.getiterator():
 if elmt.tag == 'title' and not
   elmt.text.startswith('Top Stories'):
  printf('- %s' % elmt.text)

This script runs under 2.x and 3.x without any changes and works exactly the same, although if you're using 2.4 or older, you'll need to download ElementTree separately.

But sometimes it feels like these changes are messing up your elegant Python code. After all, readability is the most important thing.

The six a compatible library, its main task is to provide the interface hides the details of a complex, you can find it in (link: http://packages.python.org/six). Whether you use a library like six or do it your way, we hope this brief introduction will get you thinking about writing code that runs under 2.x and 3.x at the same time.


Related articles: