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