1458 lines
32 KiB
ReStructuredText
1458 lines
32 KiB
ReStructuredText
.. _compatible-idioms:
|
|
|
|
Cheat Sheet: Writing Python 2-3 compatible code
|
|
===============================================
|
|
|
|
- **Copyright (c):** 2013-2019 Python Charmers Pty Ltd, Australia.
|
|
- **Author:** Ed Schofield.
|
|
- **Licence:** Creative Commons Attribution.
|
|
|
|
A PDF version is here: http://python-future.org/compatible\_idioms.pdf
|
|
|
|
This notebook shows you idioms for writing future-proof code that is
|
|
compatible with both versions of Python: 2 and 3. It accompanies Ed
|
|
Schofield's talk at PyCon AU 2014, "Writing 2/3 compatible code". (The
|
|
video is here: http://www.youtube.com/watch?v=KOqk8j11aAI&t=10m14s.)
|
|
|
|
Minimum versions:
|
|
|
|
- Python 2: 2.7+
|
|
- Python 3: 3.4+
|
|
|
|
Setup
|
|
-----
|
|
|
|
The imports below refer to these ``pip``-installable packages on PyPI:
|
|
|
|
::
|
|
|
|
import future # pip install future
|
|
import builtins # pip install future
|
|
import past # pip install future
|
|
import six # pip install six
|
|
|
|
The following scripts are also ``pip``-installable:
|
|
|
|
::
|
|
|
|
futurize # pip install future
|
|
pasteurize # pip install future
|
|
|
|
See http://python-future.org and https://pythonhosted.org/six/ for more
|
|
information.
|
|
|
|
Essential syntax differences
|
|
----------------------------
|
|
|
|
print
|
|
~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
print 'Hello'
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
print('Hello')
|
|
To print multiple strings, import ``print_function`` to prevent Py2 from
|
|
interpreting it as a tuple:
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
print 'Hello', 'Guido'
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from __future__ import print_function # (at top of module)
|
|
|
|
print('Hello', 'Guido')
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
print >> sys.stderr, 'Hello'
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from __future__ import print_function
|
|
|
|
print('Hello', file=sys.stderr)
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
print 'Hello',
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from __future__ import print_function
|
|
|
|
print('Hello', end='')
|
|
Raising exceptions
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
raise ValueError, "dodgy value"
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
raise ValueError("dodgy value")
|
|
Raising exceptions with a traceback:
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
traceback = sys.exc_info()[2]
|
|
raise ValueError, "dodgy value", traceback
|
|
.. code:: python
|
|
|
|
# Python 3 only:
|
|
raise ValueError("dodgy value").with_traceback()
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 1
|
|
from six import reraise as raise_
|
|
# or
|
|
from future.utils import raise_
|
|
|
|
traceback = sys.exc_info()[2]
|
|
raise_(ValueError, "dodgy value", traceback)
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 2
|
|
from future.utils import raise_with_traceback
|
|
|
|
raise_with_traceback(ValueError("dodgy value"))
|
|
Exception chaining (PEP 3134):
|
|
|
|
.. code:: python
|
|
|
|
# Setup:
|
|
class DatabaseError(Exception):
|
|
pass
|
|
.. code:: python
|
|
|
|
# Python 3 only
|
|
class FileDatabase:
|
|
def __init__(self, filename):
|
|
try:
|
|
self.file = open(filename)
|
|
except IOError as exc:
|
|
raise DatabaseError('failed to open') from exc
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from future.utils import raise_from
|
|
|
|
class FileDatabase:
|
|
def __init__(self, filename):
|
|
try:
|
|
self.file = open(filename)
|
|
except IOError as exc:
|
|
raise_from(DatabaseError('failed to open'), exc)
|
|
.. code:: python
|
|
|
|
# Testing the above:
|
|
try:
|
|
fd = FileDatabase('non_existent_file.txt')
|
|
except Exception as e:
|
|
assert isinstance(e.__cause__, IOError) # FileNotFoundError on Py3.3+ inherits from IOError
|
|
Catching exceptions
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
try:
|
|
...
|
|
except ValueError, e:
|
|
...
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
try:
|
|
...
|
|
except ValueError as e:
|
|
...
|
|
Division
|
|
~~~~~~~~
|
|
|
|
Integer division (rounding down):
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
assert 2 / 3 == 0
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
assert 2 // 3 == 0
|
|
"True division" (float division):
|
|
|
|
.. code:: python
|
|
|
|
# Python 3 only:
|
|
assert 3 / 2 == 1.5
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from __future__ import division # (at top of module)
|
|
|
|
assert 3 / 2 == 1.5
|
|
"Old division" (i.e. compatible with Py2 behaviour):
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
a = b / c # with any types
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from past.utils import old_div
|
|
|
|
a = old_div(b, c) # always same as / on Py2
|
|
Long integers
|
|
~~~~~~~~~~~~~
|
|
|
|
Short integers are gone in Python 3 and ``long`` has become ``int``
|
|
(without the trailing ``L`` in the ``repr``).
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only
|
|
k = 9223372036854775808L
|
|
|
|
# Python 2 and 3:
|
|
k = 9223372036854775808
|
|
.. code:: python
|
|
|
|
# Python 2 only
|
|
bigint = 1L
|
|
|
|
# Python 2 and 3
|
|
from builtins import int
|
|
bigint = int(1)
|
|
To test whether a value is an integer (of any kind):
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
if isinstance(x, (int, long)):
|
|
...
|
|
|
|
# Python 3 only:
|
|
if isinstance(x, int):
|
|
...
|
|
|
|
# Python 2 and 3: option 1
|
|
from builtins import int # subclass of long on Py2
|
|
|
|
if isinstance(x, int): # matches both int and long on Py2
|
|
...
|
|
|
|
# Python 2 and 3: option 2
|
|
from past.builtins import long
|
|
|
|
if isinstance(x, (int, long)):
|
|
...
|
|
Octal constants
|
|
~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
0644 # Python 2 only
|
|
.. code:: python
|
|
|
|
0o644 # Python 2 and 3
|
|
Backtick repr
|
|
~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
`x` # Python 2 only
|
|
.. code:: python
|
|
|
|
repr(x) # Python 2 and 3
|
|
Metaclasses
|
|
~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
class BaseForm(object):
|
|
pass
|
|
|
|
class FormType(type):
|
|
pass
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
class Form(BaseForm):
|
|
__metaclass__ = FormType
|
|
pass
|
|
.. code:: python
|
|
|
|
# Python 3 only:
|
|
class Form(BaseForm, metaclass=FormType):
|
|
pass
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from six import with_metaclass
|
|
# or
|
|
from future.utils import with_metaclass
|
|
|
|
class Form(with_metaclass(FormType, BaseForm)):
|
|
pass
|
|
Strings and bytes
|
|
-----------------
|
|
|
|
Unicode (text) string literals
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If you are upgrading an existing Python 2 codebase, it may be preferable
|
|
to mark up all string literals as unicode explicitly with ``u``
|
|
prefixes:
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only
|
|
s1 = 'The Zen of Python'
|
|
s2 = u'きたないのよりきれいな方がいい\n'
|
|
|
|
# Python 2 and 3
|
|
s1 = u'The Zen of Python'
|
|
s2 = u'きたないのよりきれいな方がいい\n'
|
|
The ``futurize`` and ``python-modernize`` tools do not currently offer
|
|
an option to do this automatically.
|
|
|
|
If you are writing code for a new project or new codebase, you can use
|
|
this idiom to make all string literals in a module unicode strings:
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 and 3
|
|
from __future__ import unicode_literals # at top of module
|
|
|
|
s1 = 'The Zen of Python'
|
|
s2 = 'きたないのよりきれいな方がいい\n'
|
|
See http://python-future.org/unicode\_literals.html for more discussion
|
|
on which style to use.
|
|
|
|
Byte-string literals
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only
|
|
s = 'This must be a byte-string'
|
|
|
|
# Python 2 and 3
|
|
s = b'This must be a byte-string'
|
|
To loop over a byte-string with possible high-bit characters, obtaining
|
|
each character as a byte-string of length 1:
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
for bytechar in 'byte-string with high-bit chars like \xf9':
|
|
...
|
|
|
|
# Python 3 only:
|
|
for myint in b'byte-string with high-bit chars like \xf9':
|
|
bytechar = bytes([myint])
|
|
|
|
# Python 2 and 3:
|
|
from builtins import bytes
|
|
for myint in bytes(b'byte-string with high-bit chars like \xf9'):
|
|
bytechar = bytes([myint])
|
|
As an alternative, ``chr()`` and ``.encode('latin-1')`` can be used to
|
|
convert an int into a 1-char byte string:
|
|
|
|
.. code:: python
|
|
|
|
# Python 3 only:
|
|
for myint in b'byte-string with high-bit chars like \xf9':
|
|
char = chr(myint) # returns a unicode string
|
|
bytechar = char.encode('latin-1')
|
|
|
|
# Python 2 and 3:
|
|
from builtins import bytes, chr
|
|
for myint in bytes(b'byte-string with high-bit chars like \xf9'):
|
|
char = chr(myint) # returns a unicode string
|
|
bytechar = char.encode('latin-1') # forces returning a byte str
|
|
basestring
|
|
~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
a = u'abc'
|
|
b = 'def'
|
|
assert (isinstance(a, basestring) and isinstance(b, basestring))
|
|
|
|
# Python 2 and 3: alternative 1
|
|
from past.builtins import basestring # pip install future
|
|
|
|
a = u'abc'
|
|
b = b'def'
|
|
assert (isinstance(a, basestring) and isinstance(b, basestring))
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 2: refactor the code to avoid considering
|
|
# byte-strings as strings.
|
|
|
|
from builtins import str
|
|
a = u'abc'
|
|
b = b'def'
|
|
c = b.decode()
|
|
assert isinstance(a, str) and isinstance(c, str)
|
|
# ...
|
|
unicode
|
|
~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
templates = [u"blog/blog_post_detail_%s.html" % unicode(slug)]
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 1
|
|
from builtins import str
|
|
templates = [u"blog/blog_post_detail_%s.html" % str(slug)]
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 2
|
|
from builtins import str as text
|
|
templates = [u"blog/blog_post_detail_%s.html" % text(slug)]
|
|
StringIO
|
|
~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
from StringIO import StringIO
|
|
# or:
|
|
from cStringIO import StringIO
|
|
|
|
# Python 2 and 3:
|
|
from io import BytesIO # for handling byte strings
|
|
from io import StringIO # for handling unicode strings
|
|
Imports relative to a package
|
|
-----------------------------
|
|
|
|
Suppose the package is:
|
|
|
|
::
|
|
|
|
mypackage/
|
|
__init__.py
|
|
submodule1.py
|
|
submodule2.py
|
|
|
|
|
|
and the code below is in ``submodule1.py``:
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
import submodule2
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from . import submodule2
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
# To make Py2 code safer (more like Py3) by preventing
|
|
# implicit relative imports, you can also add this to the top:
|
|
from __future__ import absolute_import
|
|
Dictionaries
|
|
------------
|
|
|
|
.. code:: python
|
|
|
|
heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}
|
|
Iterating through ``dict`` keys/values/items
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Iterable dict keys:
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
for key in heights.iterkeys():
|
|
...
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
for key in heights:
|
|
...
|
|
Iterable dict values:
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
for value in heights.itervalues():
|
|
...
|
|
.. code:: python
|
|
|
|
# Idiomatic Python 3
|
|
for value in heights.values(): # extra memory overhead on Py2
|
|
...
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 1
|
|
from builtins import dict
|
|
|
|
heights = dict(Fred=175, Anne=166, Joe=192)
|
|
for key in heights.values(): # efficient on Py2 and Py3
|
|
...
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 2
|
|
from future.utils import itervalues
|
|
# or
|
|
from six import itervalues
|
|
|
|
for key in itervalues(heights):
|
|
...
|
|
Iterable dict items:
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
for (key, value) in heights.iteritems():
|
|
...
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 1
|
|
for (key, value) in heights.items(): # inefficient on Py2
|
|
...
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 2
|
|
from future.utils import viewitems
|
|
|
|
for (key, value) in viewitems(heights): # also behaves like a set
|
|
...
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 3
|
|
from future.utils import iteritems
|
|
# or
|
|
from six import iteritems
|
|
|
|
for (key, value) in iteritems(heights):
|
|
...
|
|
dict keys/values/items as a list
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
dict keys as a list:
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
keylist = heights.keys()
|
|
assert isinstance(keylist, list)
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
keylist = list(heights)
|
|
assert isinstance(keylist, list)
|
|
dict values as a list:
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}
|
|
valuelist = heights.values()
|
|
assert isinstance(valuelist, list)
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 1
|
|
valuelist = list(heights.values()) # inefficient on Py2
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 2
|
|
from builtins import dict
|
|
|
|
heights = dict(Fred=175, Anne=166, Joe=192)
|
|
valuelist = list(heights.values())
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 3
|
|
from future.utils import listvalues
|
|
|
|
valuelist = listvalues(heights)
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 4
|
|
from future.utils import itervalues
|
|
# or
|
|
from six import itervalues
|
|
|
|
valuelist = list(itervalues(heights))
|
|
dict items as a list:
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 1
|
|
itemlist = list(heights.items()) # inefficient on Py2
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 2
|
|
from future.utils import listitems
|
|
|
|
itemlist = listitems(heights)
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 3
|
|
from future.utils import iteritems
|
|
# or
|
|
from six import iteritems
|
|
|
|
itemlist = list(iteritems(heights))
|
|
Custom class behaviour
|
|
----------------------
|
|
|
|
Custom iterators
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only
|
|
class Upper(object):
|
|
def __init__(self, iterable):
|
|
self._iter = iter(iterable)
|
|
def next(self): # Py2-style
|
|
return self._iter.next().upper()
|
|
def __iter__(self):
|
|
return self
|
|
|
|
itr = Upper('hello')
|
|
assert itr.next() == 'H' # Py2-style
|
|
assert list(itr) == list('ELLO')
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 1
|
|
from builtins import object
|
|
|
|
class Upper(object):
|
|
def __init__(self, iterable):
|
|
self._iter = iter(iterable)
|
|
def __next__(self): # Py3-style iterator interface
|
|
return next(self._iter).upper() # builtin next() function calls
|
|
def __iter__(self):
|
|
return self
|
|
|
|
itr = Upper('hello')
|
|
assert next(itr) == 'H' # compatible style
|
|
assert list(itr) == list('ELLO')
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 2
|
|
from future.utils import implements_iterator
|
|
|
|
@implements_iterator
|
|
class Upper(object):
|
|
def __init__(self, iterable):
|
|
self._iter = iter(iterable)
|
|
def __next__(self): # Py3-style iterator interface
|
|
return next(self._iter).upper() # builtin next() function calls
|
|
def __iter__(self):
|
|
return self
|
|
|
|
itr = Upper('hello')
|
|
assert next(itr) == 'H'
|
|
assert list(itr) == list('ELLO')
|
|
Custom ``__str__`` methods
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
class MyClass(object):
|
|
def __unicode__(self):
|
|
return 'Unicode string: \u5b54\u5b50'
|
|
def __str__(self):
|
|
return unicode(self).encode('utf-8')
|
|
|
|
a = MyClass()
|
|
print(a) # prints encoded string
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from future.utils import python_2_unicode_compatible
|
|
|
|
@python_2_unicode_compatible
|
|
class MyClass(object):
|
|
def __str__(self):
|
|
return u'Unicode string: \u5b54\u5b50'
|
|
|
|
a = MyClass()
|
|
print(a) # prints string encoded as utf-8 on Py2
|
|
|
|
.. parsed-literal::
|
|
|
|
Unicode string: 孔子
|
|
|
|
|
|
Custom ``__nonzero__`` vs ``__bool__`` method:
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
class AllOrNothing(object):
|
|
def __init__(self, l):
|
|
self.l = l
|
|
def __nonzero__(self):
|
|
return all(self.l)
|
|
|
|
container = AllOrNothing([0, 100, 200])
|
|
assert not bool(container)
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from builtins import object
|
|
|
|
class AllOrNothing(object):
|
|
def __init__(self, l):
|
|
self.l = l
|
|
def __bool__(self):
|
|
return all(self.l)
|
|
|
|
container = AllOrNothing([0, 100, 200])
|
|
assert not bool(container)
|
|
Lists versus iterators
|
|
----------------------
|
|
|
|
xrange
|
|
~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
for i in xrange(10**8):
|
|
...
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: forward-compatible
|
|
from builtins import range
|
|
for i in range(10**8):
|
|
...
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: backward-compatible
|
|
from past.builtins import xrange
|
|
for i in xrange(10**8):
|
|
...
|
|
range
|
|
~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only
|
|
mylist = range(5)
|
|
assert mylist == [0, 1, 2, 3, 4]
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: forward-compatible: option 1
|
|
mylist = list(range(5)) # copies memory on Py2
|
|
assert mylist == [0, 1, 2, 3, 4]
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: forward-compatible: option 2
|
|
from builtins import range
|
|
|
|
mylist = list(range(5))
|
|
assert mylist == [0, 1, 2, 3, 4]
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 3
|
|
from future.utils import lrange
|
|
|
|
mylist = lrange(5)
|
|
assert mylist == [0, 1, 2, 3, 4]
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: backward compatible
|
|
from past.builtins import range
|
|
|
|
mylist = range(5)
|
|
assert mylist == [0, 1, 2, 3, 4]
|
|
map
|
|
~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
mynewlist = map(f, myoldlist)
|
|
assert mynewlist == [f(x) for x in myoldlist]
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 1
|
|
# Idiomatic Py3, but inefficient on Py2
|
|
mynewlist = list(map(f, myoldlist))
|
|
assert mynewlist == [f(x) for x in myoldlist]
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 2
|
|
from builtins import map
|
|
|
|
mynewlist = list(map(f, myoldlist))
|
|
assert mynewlist == [f(x) for x in myoldlist]
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 3
|
|
try:
|
|
import itertools.imap as map
|
|
except ImportError:
|
|
pass
|
|
|
|
mynewlist = list(map(f, myoldlist)) # inefficient on Py2
|
|
assert mynewlist == [f(x) for x in myoldlist]
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 4
|
|
from future.utils import lmap
|
|
|
|
mynewlist = lmap(f, myoldlist)
|
|
assert mynewlist == [f(x) for x in myoldlist]
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 5
|
|
from past.builtins import map
|
|
|
|
mynewlist = map(f, myoldlist)
|
|
assert mynewlist == [f(x) for x in myoldlist]
|
|
imap
|
|
~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
from itertools import imap
|
|
|
|
myiter = imap(func, myoldlist)
|
|
assert isinstance(myiter, iter)
|
|
.. code:: python
|
|
|
|
# Python 3 only:
|
|
myiter = map(func, myoldlist)
|
|
assert isinstance(myiter, iter)
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 1
|
|
from builtins import map
|
|
|
|
myiter = map(func, myoldlist)
|
|
assert isinstance(myiter, iter)
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 2
|
|
try:
|
|
import itertools.imap as map
|
|
except ImportError:
|
|
pass
|
|
|
|
myiter = map(func, myoldlist)
|
|
assert isinstance(myiter, iter)
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 3
|
|
from six.moves import map
|
|
|
|
myiter = map(func, myoldlist)
|
|
assert isinstance(myiter, iter)
|
|
|
|
zip, izip
|
|
~~~~~~~~~
|
|
|
|
As above with ``zip`` and ``itertools.izip``.
|
|
|
|
filter, ifilter
|
|
~~~~~~~~~~~~~~~
|
|
|
|
As above with ``filter`` and ``itertools.ifilter`` too.
|
|
|
|
Other builtins
|
|
--------------
|
|
|
|
File IO with open()
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only
|
|
f = open('myfile.txt')
|
|
data = f.read() # as a byte string
|
|
text = data.decode('utf-8')
|
|
|
|
# Python 2 and 3: alternative 1
|
|
from io import open
|
|
f = open('myfile.txt', 'rb')
|
|
data = f.read() # as bytes
|
|
text = data.decode('utf-8') # unicode, not bytes
|
|
|
|
# Python 2 and 3: alternative 2
|
|
from io import open
|
|
f = open('myfile.txt', encoding='utf-8')
|
|
text = f.read() # unicode, not bytes
|
|
reduce()
|
|
~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
assert reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) == 1+2+3+4+5
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from functools import reduce
|
|
|
|
assert reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) == 1+2+3+4+5
|
|
raw\_input()
|
|
~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
name = raw_input('What is your name? ')
|
|
assert isinstance(name, str) # native str
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from builtins import input
|
|
|
|
name = input('What is your name? ')
|
|
assert isinstance(name, str) # native str on Py2 and Py3
|
|
input()
|
|
~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
input("Type something safe please: ")
|
|
.. code:: python
|
|
|
|
# Python 2 and 3
|
|
from builtins import input
|
|
eval(input("Type something safe please: "))
|
|
Warning: using either of these is **unsafe** with untrusted input.
|
|
|
|
file()
|
|
~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
f = file(pathname)
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
f = open(pathname)
|
|
|
|
# But preferably, use this:
|
|
from io import open
|
|
f = open(pathname, 'rb') # if f.read() should return bytes
|
|
# or
|
|
f = open(pathname, 'rt') # if f.read() should return unicode text
|
|
exec
|
|
~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
exec 'x = 10'
|
|
|
|
# Python 2 and 3:
|
|
exec('x = 10')
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
g = globals()
|
|
exec 'x = 10' in g
|
|
|
|
# Python 2 and 3:
|
|
g = globals()
|
|
exec('x = 10', g)
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
l = locals()
|
|
exec 'x = 10' in g, l
|
|
|
|
# Python 2 and 3:
|
|
exec('x = 10', g, l)
|
|
execfile()
|
|
~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
execfile('myfile.py')
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 1
|
|
from past.builtins import execfile
|
|
|
|
execfile('myfile.py')
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 2
|
|
exec(compile(open('myfile.py').read()))
|
|
|
|
# This can sometimes cause this:
|
|
# SyntaxError: function ... uses import * and bare exec ...
|
|
# See https://github.com/PythonCharmers/python-future/issues/37
|
|
unichr()
|
|
~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
assert unichr(8364) == '€'
|
|
.. code:: python
|
|
|
|
# Python 3 only:
|
|
assert chr(8364) == '€'
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from builtins import chr
|
|
assert chr(8364) == '€'
|
|
intern()
|
|
~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
intern('mystring')
|
|
.. code:: python
|
|
|
|
# Python 3 only:
|
|
from sys import intern
|
|
intern('mystring')
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 1
|
|
from past.builtins import intern
|
|
intern('mystring')
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 2
|
|
from six.moves import intern
|
|
intern('mystring')
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 3
|
|
from future.standard_library import install_aliases
|
|
install_aliases()
|
|
from sys import intern
|
|
intern('mystring')
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 2
|
|
try:
|
|
from sys import intern
|
|
except ImportError:
|
|
pass
|
|
intern('mystring')
|
|
apply()
|
|
~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
args = ('a', 'b')
|
|
kwargs = {'kwarg1': True}
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
apply(f, args, kwargs)
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 1
|
|
f(*args, **kwargs)
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 2
|
|
from past.builtins import apply
|
|
apply(f, args, kwargs)
|
|
chr()
|
|
~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
assert chr(64) == b'@'
|
|
assert chr(200) == b'\xc8'
|
|
.. code:: python
|
|
|
|
# Python 3 only: option 1
|
|
assert chr(64).encode('latin-1') == b'@'
|
|
assert chr(0xc8).encode('latin-1') == b'\xc8'
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 1
|
|
from builtins import chr
|
|
|
|
assert chr(64).encode('latin-1') == b'@'
|
|
assert chr(0xc8).encode('latin-1') == b'\xc8'
|
|
.. code:: python
|
|
|
|
# Python 3 only: option 2
|
|
assert bytes([64]) == b'@'
|
|
assert bytes([0xc8]) == b'\xc8'
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: option 2
|
|
from builtins import bytes
|
|
|
|
assert bytes([64]) == b'@'
|
|
assert bytes([0xc8]) == b'\xc8'
|
|
cmp()
|
|
~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 1
|
|
from past.builtins import cmp
|
|
assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 2
|
|
cmp = lambda(x, y): (x > y) - (x < y)
|
|
assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
|
|
reload()
|
|
~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
reload(mymodule)
|
|
.. code:: python
|
|
|
|
# Python 2 and 3
|
|
from imp import reload
|
|
reload(mymodule)
|
|
Standard library
|
|
----------------
|
|
|
|
dbm modules
|
|
~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only
|
|
import anydbm
|
|
import whichdb
|
|
import dbm
|
|
import dumbdbm
|
|
import gdbm
|
|
|
|
# Python 2 and 3: alternative 1
|
|
from future import standard_library
|
|
standard_library.install_aliases()
|
|
|
|
import dbm
|
|
import dbm.ndbm
|
|
import dbm.dumb
|
|
import dbm.gnu
|
|
|
|
# Python 2 and 3: alternative 2
|
|
from future.moves import dbm
|
|
from future.moves.dbm import dumb
|
|
from future.moves.dbm import ndbm
|
|
from future.moves.dbm import gnu
|
|
|
|
# Python 2 and 3: alternative 3
|
|
from six.moves import dbm_gnu
|
|
# (others not supported)
|
|
commands / subprocess modules
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only
|
|
from commands import getoutput, getstatusoutput
|
|
|
|
# Python 2 and 3
|
|
from future import standard_library
|
|
standard_library.install_aliases()
|
|
|
|
from subprocess import getoutput, getstatusoutput
|
|
StringIO module
|
|
~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only
|
|
from StringIO import StringIO
|
|
from cStringIO import StringIO
|
|
.. code:: python
|
|
|
|
# Python 2 and 3
|
|
from io import BytesIO
|
|
# and refactor StringIO() calls to BytesIO() if passing byte-strings
|
|
http module
|
|
~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
import httplib
|
|
import Cookie
|
|
import cookielib
|
|
import BaseHTTPServer
|
|
import SimpleHTTPServer
|
|
import CGIHttpServer
|
|
|
|
# Python 2 and 3 (after ``pip install future``):
|
|
import http.client
|
|
import http.cookies
|
|
import http.cookiejar
|
|
import http.server
|
|
xmlrpc module
|
|
~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
import DocXMLRPCServer
|
|
import SimpleXMLRPCServer
|
|
|
|
# Python 2 and 3 (after ``pip install future``):
|
|
import xmlrpc.server
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
import xmlrpclib
|
|
|
|
# Python 2 and 3 (after ``pip install future``):
|
|
import xmlrpc.client
|
|
html escaping and entities
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 and 3:
|
|
from cgi import escape
|
|
|
|
# Safer (Python 2 and 3, after ``pip install future``):
|
|
from html import escape
|
|
|
|
# Python 2 only:
|
|
from htmlentitydefs import codepoint2name, entitydefs, name2codepoint
|
|
|
|
# Python 2 and 3 (after ``pip install future``):
|
|
from html.entities import codepoint2name, entitydefs, name2codepoint
|
|
html parsing
|
|
~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
from HTMLParser import HTMLParser
|
|
|
|
# Python 2 and 3 (after ``pip install future``)
|
|
from html.parser import HTMLParser
|
|
|
|
# Python 2 and 3 (alternative 2):
|
|
from future.moves.html.parser import HTMLParser
|
|
urllib module
|
|
~~~~~~~~~~~~~
|
|
|
|
``urllib`` is the hardest module to use from Python 2/3 compatible code.
|
|
You might want to switch to Requests (http://python-requests.org) instead.
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
from urlparse import urlparse
|
|
from urllib import urlencode
|
|
from urllib2 import urlopen, Request, HTTPError
|
|
.. code:: python
|
|
|
|
# Python 3 only:
|
|
from urllib.parse import urlparse, urlencode
|
|
from urllib.request import urlopen, Request
|
|
from urllib.error import HTTPError
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: easiest option
|
|
from future.standard_library import install_aliases
|
|
install_aliases()
|
|
|
|
from urllib.parse import urlparse, urlencode
|
|
from urllib.request import urlopen, Request
|
|
from urllib.error import HTTPError
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 2
|
|
from future.standard_library import hooks
|
|
|
|
with hooks():
|
|
from urllib.parse import urlparse, urlencode
|
|
from urllib.request import urlopen, Request
|
|
from urllib.error import HTTPError
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 3
|
|
from future.moves.urllib.parse import urlparse, urlencode
|
|
from future.moves.urllib.request import urlopen, Request
|
|
from future.moves.urllib.error import HTTPError
|
|
# or
|
|
from six.moves.urllib.parse import urlparse, urlencode
|
|
from six.moves.urllib.request import urlopen
|
|
from six.moves.urllib.error import HTTPError
|
|
.. code:: python
|
|
|
|
# Python 2 and 3: alternative 4
|
|
try:
|
|
from urllib.parse import urlparse, urlencode
|
|
from urllib.request import urlopen, Request
|
|
from urllib.error import HTTPError
|
|
except ImportError:
|
|
from urlparse import urlparse
|
|
from urllib import urlencode
|
|
from urllib2 import urlopen, Request, HTTPError
|
|
Tkinter
|
|
~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
import Tkinter
|
|
import Dialog
|
|
import FileDialog
|
|
import ScrolledText
|
|
import SimpleDialog
|
|
import Tix
|
|
import Tkconstants
|
|
import Tkdnd
|
|
import tkColorChooser
|
|
import tkCommonDialog
|
|
import tkFileDialog
|
|
import tkFont
|
|
import tkMessageBox
|
|
import tkSimpleDialog
|
|
import ttk
|
|
|
|
# Python 2 and 3 (after ``pip install future``):
|
|
import tkinter
|
|
import tkinter.dialog
|
|
import tkinter.filedialog
|
|
import tkinter.scrolledtext
|
|
import tkinter.simpledialog
|
|
import tkinter.tix
|
|
import tkinter.constants
|
|
import tkinter.dnd
|
|
import tkinter.colorchooser
|
|
import tkinter.commondialog
|
|
import tkinter.filedialog
|
|
import tkinter.font
|
|
import tkinter.messagebox
|
|
import tkinter.simpledialog
|
|
import tkinter.ttk
|
|
socketserver
|
|
~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
import SocketServer
|
|
|
|
# Python 2 and 3 (after ``pip install future``):
|
|
import socketserver
|
|
copy\_reg, copyreg
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
import copy_reg
|
|
|
|
# Python 2 and 3 (after ``pip install future``):
|
|
import copyreg
|
|
configparser
|
|
~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
from ConfigParser import ConfigParser
|
|
|
|
# Python 2 and 3 (after ``pip install configparser``):
|
|
from configparser import ConfigParser
|
|
queue
|
|
~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
from Queue import Queue, heapq, deque
|
|
|
|
# Python 2 and 3 (after ``pip install future``):
|
|
from queue import Queue, heapq, deque
|
|
repr, reprlib
|
|
~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
from repr import aRepr, repr
|
|
|
|
# Python 2 and 3 (after ``pip install future``):
|
|
from reprlib import aRepr, repr
|
|
UserDict, UserList, UserString
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
from UserDict import UserDict
|
|
from UserList import UserList
|
|
from UserString import UserString
|
|
|
|
# Python 3 only:
|
|
from collections import UserDict, UserList, UserString
|
|
|
|
# Python 2 and 3: alternative 1
|
|
from future.moves.collections import UserDict, UserList, UserString
|
|
|
|
# Python 2 and 3: alternative 2
|
|
from six.moves import UserDict, UserList, UserString
|
|
|
|
# Python 2 and 3: alternative 3
|
|
from future.standard_library import install_aliases
|
|
install_aliases()
|
|
from collections import UserDict, UserList, UserString
|
|
itertools: filterfalse, zip\_longest
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. code:: python
|
|
|
|
# Python 2 only:
|
|
from itertools import ifilterfalse, izip_longest
|
|
|
|
# Python 3 only:
|
|
from itertools import filterfalse, zip_longest
|
|
|
|
# Python 2 and 3: alternative 1
|
|
from future.moves.itertools import filterfalse, zip_longest
|
|
|
|
# Python 2 and 3: alternative 2
|
|
from six.moves import filterfalse, zip_longest
|
|
|
|
# Python 2 and 3: alternative 3
|
|
from future.standard_library import install_aliases
|
|
install_aliases()
|
|
from itertools import filterfalse, zip_longest
|