{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Pythonic APIs: the workshop notebook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tutorial overview\n", "\n", "* Introduction\n", "* A simple but full-featured Pythonic class\n", " * **Exercise:** custom formatting and alternate constructor\n", "* A Pythonic sequence\n", " * **Exercise:** implementing sequence behavior\n", "* *Coffee break*\n", "* A Pythonic sequence (continued)\n", " * **Exercise:** custom formatting\n", "* Operator overloading\n", " * **Exercise:** implement `@` for dot product\n", "* Wrap-up\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What is *Pythonic*?\n", "\n", "Pythonic code is concise and expressive. It leverages Python features and idioms to acomplish maximum effect with minimum effort, without being unreadable. It uses the language as it's designed to be used, so it is most readable to the fluent Pythonista." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Real example 1: the `requests` API\n", "\n", "`requests` is pleasant HTTP client library. It's great but it would be awesome if it was asynchronous (but could it be pleasant **and** asynchronous at the same time?). The examples below are from Kenneth Reitz, the author of `requests` ([source](https://gist.github.com/kennethreitz/973705)).\n", "\n", "#### Pythonic, using `requests`\n", "\n", "```python\n", "import requests\n", "\n", "r = requests.get('https://api.github.com', auth=('user', 'pass'))\n", "\n", "print r.status_code\n", "print r.headers['content-type']\n", "\n", "# ------\n", "# 200\n", "# 'application/json'\n", "```\n", "\n", "#### Unpythonic, using `urllib2`\n", "\n", "```python\n", "import urllib2\n", "\n", "gh_url = 'https://api.github.com'\n", "\n", "req = urllib2.Request(gh_url)\n", "\n", "password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()\n", "password_manager.add_password(None, gh_url, 'user', 'pass')\n", "\n", "auth_manager = urllib2.HTTPBasicAuthHandler(password_manager)\n", "opener = urllib2.build_opener(auth_manager)\n", "\n", "urllib2.install_opener(opener)\n", "\n", "handler = urllib2.urlopen(req)\n", "\n", "print handler.getcode()\n", "print handler.headers.getheader('content-type')\n", "\n", "# ------\n", "# 200\n", "# 'application/json'\n", "\n", "```\n" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "### Real example 2: classes are optional in `py.test` and `nosetests`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Features of idiomaitc Python APIs\n", "\n", "* Let the user apply previous knowledge of the standard types and operations\n", "* Make it easy to leverage existing libraries\n", "* Come with “batteries included”\n", "* Use duck typing for enhanced interoperation with user-defined types\n", "* Provide ready to use objects (no instantiation needed)\n", "* Don't require subclassing for basic usage\n", "* Leverage standard language objects: containers, functions, classes, modules\n", "* Make proper use of the Data Model (i.e. special methods)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Introduction\n", "\n", "One of the keys to consistent, *Pythonic*, behavior in Python is understanding and leveraging the **Data Model**. The Python Data Model defines standard APIs which enable...\n", "\n", "### Iteration" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['F', 'l', 'u', 'e', 'n', 't']\n", "(10, 20, 50)\n", "10 20 30 40 50 " ] } ], "source": [ "s = 'Fluent'\n", "L = [10, 20, 30, 40, 50]\n", "\n", "print(list(s)) # list constructor iterates over its argument\n", "\n", "a, b, *middle, c = L # tuple unpacking iterates over right side\n", "print((a, b, c))\n", "\n", "for i in L:\n", " print(i, end=' ')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sizing with `len()`" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(6, 5)" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(s), len(L)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(6, 5)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s.__len__(), L.__len__()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Arithmetic" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(6, 6)" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 2\n", "b = 3\n", "a * b, a.__mul__(b)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, [...]]" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "L = [1, 2, 3]\n", "L.append(L)\n", "L" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## A simple but full-featured Pythonic class" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## String formatting mini-language" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "1.4142135623730951" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = 2**.5\n", "x" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'1.414'" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(x, '.3f')" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2016-05-11 20:53:37.210333\n", "20:53\n" ] } ], "source": [ "from datetime import datetime\n", "agora = datetime.now()\n", "print(agora)\n", "print(format(agora, '%H:%M'))" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'20... 1.414!'" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'{1:%H}... {0:.3f}!'.format(x, agora)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "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.5.1" } }, "nbformat": 4, "nbformat_minor": 0 }