Interfaces in Python

A work-in-progress reference all about interfaces in Python.

Feel free to give me feedback on the project page

My PyCon talk on interfaces:
slides <https://docs.google.com/present/...> on the PyCon site <https://us.pycon.org/2012/schedule/presentation/126/>

understanding_software/interfaces

Abstract API Model Approaches

Python

Informally specified protocols, facilitated by duck-typing, have been the mainstay of Python since the beginning. Since 2.6/3.0 abstract base classes have been available as a formal means of specifying interfaces. Other proposals have come and gone.

Python also makes it pretty easy to build-your-own interface system, as evidenced by the variety of solutions out there.

Data Types

Python

Python is a strongly-typed, dynamically-typed, interpreted language. Let’s take a look at the data model a bit more.

Class-based Approaches

Protocols

Python

This is the bread and butter of Python’s interfaces.

Abstract Base Classes

Python

Abstract base classes have been a part of Python since the Py3k efforts led to PEP 3119 in 2007.

Python’s Data Model

http://docs.python.org/dev/reference/datamodel.html:

Objects are Python.s abstraction for data. All data in a Python
program is represented by objects or by relations between objects.

So, everything is an object in Python, including modules, classes, and literals. Every object is an instance of the base object type or of a subclass thereof:

class X:
    pass

isinstance(object(), object) == True
isinstance(object, object) == True
isinstance("abc", object) == True
isinstance(1, object) == True
isinstance(X, object) == True
isinstance(X(), object) == True
isinstance(type, object) == True

Every object has a type and every type is an instance of the base type:

class MetaY(type):
    pass
class Y(metaclass=MetaY)

type(object()) == object
type(object) == type
isinstance(object, type) == True

type("abc") == str
isinstance(str, type) == True

type(1) == int
isinstance(int, type) == True

type(X()) == X
type(X) == type
isinstance(X, type) == True

type(Y) == MetaY
isinstance(Y, type) == True

type(type) == type
isinstance(type, type) == True

Be sure to notice the special-cased nature of the base object and base type:

type(type) == type
isinstance(type, object) == True

type(object) == type
isinstance(object, object) == True

Python’s Dynamic Typing

Names don’t have type declarations, like they do in statically-typed languages. You could also look at it like all names have the same implicit type declaration: object. Either way, any object can be bound to any valid name (including as a function argument).

Objects are bound to names. Names are not bound to objects. As a consequence, objects do not “know” the names to which they are bound.

Duck-typing

“Polymorphism without inheritance”

http://en.wikipedia.org/wiki/Duck_typing

Duck-typing is polymorphism by capability, as opposed to polymorphism by type.

  • “signature-based” polymorphism <http://zephyrfalcon.org/labs/beginners_mistakes.html>
  • Requiring a specific interface instead of a specific type. <>
  • Determining an object’s type by inspection of its method / attribute signature rather than by explicit relationship to some type object. <>
  • Even without formal interface declarations, good practice mostly depends on conformant interfaces rather than subclassing to determine an object’s type. <>

Python has always been about what an object can do, rather than its type. This has changed somewhat with the advent of abstract base classes (see PEP 3119), where isinstance checks lessen the performance hit of LBYL (see below).

Examples

Duck-typing is all about the attributes an object has and what that object can do. For example:

# LBYL
if hasattr(obj, "quack"):
    obj.quack()

#EAFP
try:
    quack = obj.quack
except Exception:
    ...
quack()

#EAFP - just try it
obj.quack()

This is not duck-typing (though perfectly valid):

# LBYL
if isinstance(obj, Duck):
    obj.quack()

# LBYL
if implements(obj, Duck):
    obj.quack()

Python’s Protocols

While duck-typing is an integral part of writing Python, the application of it in the language itself is a key part of understanding how Python works under the hood.

...

collections.abc

Writing Your Own ABC