Usage

Declaring Interfaces

An interface describes a collection of metrhods and properties that should be provided by implementors.

We implement an interface by subclassing from interface.Interface and defining stubs for methods that should be part of the interface:

class MyInterface(interface.Interface):

    def method1(self, x, y, z):
        pass

    def method2(self):
        pass

Implementing Interfaces

To declare that a class implements an interface I, that class should subclass from implements(I):

class MyClass(interface.implements(MyInterface)):

    def method1(self, x, y, z):
        return x + y + z

    def method2(self):
        return "foo"

A class can implement more than one interface:

class MathStudent(Interface):

    def argue(self, topic):
        pass

    def calculate(self, x, y):
        pass


class PhilosophyStudent(Interface):

    def argue(self, topic):
        pass

    def pontificate(self):
        pass


class LiberalArtsStudent(implements(MathStudent, PhilosophyStudent)):

    def argue(self, topic):
        print(topic, "is", ["good", "bad"][random.choice([0, 1])])

    def calculate(self, x, y):
        return x + y

    def pontificate(self):
        print("I think what Wittgenstein was **really** saying is...")

Notice that interfaces can have intersecting methods as long as their signatures match.

Properties

Interfaces can declare non-method attributes that should be provided by implementations using property:

class MyInterface(interface.Interface):

    @property
    def my_property(self):
        pass

Implementations are required to provide a property with the same name.

class MyClass(interface.implements(MyInterface)):

    @property
    def my_property(self):
        return 3

Default Implementations

Sometimes we have a method that should be part of an interface, but which can be implemented in terms of other interface methods. When this happens, you can use interface.default to provide a default implementation of a method.

class ReadOnlyMapping(interface.Interface):

    def get(self, key):
        pass

    def keys(self):
        pass

    @interface.default
    def get_all(self):
        out = {}
        for k in self.keys():
            out[k] = self.get(k)
        return out

Implementors are not required to implement methods with defaults:

class MyReadOnlyMapping(interface.implements(ReadOnlyMapping)):
    def __init__(self, data):
        self._data = data

    def get(self, key):
        return self._data[key]

    def keys(self):
        return self._data.keys()

    # get_all(self) will automatically be copied from the interface default.

Default implementations should always be implemented in terms of other interface methods.

In Python 3, default will show a warning if a default implementation uses non-interface members of an object:

class ReadOnlyMapping(interface.Interface):

    def get(self, key):
        pass

    def keys(self):
        pass

    @interface.default
    def get_all(self):
        # This is supposed to be a default implementation for **any**
        # ReadOnlyMapping, but this implementation assumes that 'self' has
        # an _data attribute that isn't part of the interface!
        return self._data.keys()

Running the above example displays a warning about the default implementation of get_all:

$ python example.py
example.py:4: UnsafeDefault: Default for ReadOnlyMapping.get_all uses non-interface attributes.

The following attributes are used but are not part of the interface:
  - _data

Consider changing ReadOnlyMapping.get_all or making these attributes part of ReadOnlyMapping.
   class ReadOnlyMapping(interface.Interface):