Pythonic APIs: the workshop notebook

Tutorial overview

  • Introduction
  • A simple but full-featured Pythonic class
    • Exercise: custom formatting and alternate constructor
  • A Pythonic sequence
    • Exercise: implementing sequence behavior
  • Coffee break
  • A Pythonic sequence (continued)
    • Exercise: custom formatting
  • Operator overloading
    • Exercise: implement @ for dot product
  • Wrap-up

What is Pythonic?

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.

Real example 1: the requests API

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).

Pythonic, using requests

import requests

r = requests.get('https://api.github.com', auth=('user', 'pass'))

print r.status_code
print r.headers['content-type']

# ------
# 200
# 'application/json'

Unpythonic, using urllib2

import urllib2

gh_url = 'https://api.github.com'

req = urllib2.Request(gh_url)

password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
password_manager.add_password(None, gh_url, 'user', 'pass')

auth_manager = urllib2.HTTPBasicAuthHandler(password_manager)
opener = urllib2.build_opener(auth_manager)

urllib2.install_opener(opener)

handler = urllib2.urlopen(req)

print handler.getcode()
print handler.headers.getheader('content-type')

# ------
# 200
# 'application/json'

Real example 2: classes are optional in py.test and nosetests

Features of idiomaitc Python APIs

  • Let the user apply previous knowledge of the standard types and operations
  • Make it easy to leverage existing libraries
  • Come with “batteries included”
  • Use duck typing for enhanced interoperation with user-defined types
  • Provide ready to use objects (no instantiation needed)
  • Don't require subclassing for basic usage
  • Leverage standard language objects: containers, functions, classes, modules
  • Make proper use of the Data Model (i.e. special methods)

Introduction

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...

Iteration


In [19]:
s = 'Fluent'
L = [10, 20, 30, 40, 50]

print(list(s))  # list constructor iterates over its argument

a, b, *middle, c = L  # tuple unpacking iterates over right side
print((a, b, c))

for i in L:
    print(i, end=' ')


['F', 'l', 'u', 'e', 'n', 't']
(10, 20, 50)
10 20 30 40 50 

Sizing with len()


In [3]:
len(s), len(L)


Out[3]:
(6, 5)

In [4]:
s.__len__(), L.__len__()


Out[4]:
(6, 5)

Arithmetic


In [10]:
a = 2
b = 3
a * b, a.__mul__(b)


Out[10]:
(6, 6)

In [24]:
L = [1, 2, 3]
L.append(L)
L


Out[24]:
[1, 2, 3, [...]]

String formatting mini-language


In [27]:
x = 2**.5
x


Out[27]:
1.4142135623730951

In [29]:
format(x, '.3f')


Out[29]:
'1.414'

In [31]:
from datetime import datetime
agora = datetime.now()
print(agora)
print(format(agora, '%H:%M'))


2016-05-11 20:53:37.210333
20:53

In [34]:
'{1:%H}... {0:.3f}!'.format(x, agora)


Out[34]:
'20... 1.414!'

In [ ]: