1.1.4 Defining and Subclassing Interfaces

The easiest way to define an interface with the protocols package is to subclass protocols.Interface. Interface does not supply any data or methods of its own, so you are free to define whatever you need. There are two common styles of defining interfaces, illustrated below:


\begin{verbatim}from protocols import Interface, AbstractBase
\par
...

The ``pure'' style emphasizes the interface as seen by the caller, and is not intended to be subclassed for implementation. Notice that the self parameter is not included in its method definitions, because self is not supplied when calling the methods. The ``ABC'' style, on the other hand, emphasizes implementation, as it is intended to be subclassed for that purpose. Therefore, it includes method bodies, even for abstract methods. Each style has different uses: ``ABC'' is a popular rapid development style, while the ``pure'' approach has some distinct documentation advantages.

protocols.AbstractBase may be used as a base class for either style, but protocols.Interface is only usable for the "pure" interface style, as it supports the convenience adaptation API (see section 1.1.3).

(Note: both base classes use an explicit metaclass, so keep in mind that if you want to subclass an abstract base for implementation using a different metaclass, you may need to create a third metaclass that combines protocols.AbstractBaseMeta with your desired metaclass.)

Subclassing a subclass of Interface (or AbstractBase) creates a new interface (or ABC) that implies the first interface (or ABC). This means that any object that supports the second interface (or ABC), is considered to implicitly support the first interface (or ABC). For example:


\begin{verbatim}class IReadWriteMapping(IReadMapping):
\par
''''''Mapping with g...
...__(key,value):
''''''Store value for key, return None''''''
\par
\end{verbatim}

The IReadWriteMapping interface implies the IReadMapping interface. Therefore, any object that supports IReadWriteMapping is understood to also support the IReadMapping interface. The reverse, however, is not true.

Inheritance is only one way to declare that one interface implies another, however, and its uses are limited. Let's say for example, that some package A supplies objects that support IReadWriteMapping, while package B needs objects that support IReadMapping. But each package declared its own interface, neither inheriting from the other.

As developers reading the documentation of these interfaces, it is obvious to us that IReadWriteMapping implies IReadMapping, because we understand what they do. But there is no way for Python to know this, unless we explicitly state it, like this:


\begin{verbatim}import protocols
from A import IReadWriteMapping
from B import I...
... provides = [IReadMapping],
forProtocols = [IReadWriteMapping]
)
\end{verbatim}

In the above example, we use the protocols declaration API to say that no adapter is needed to support the B.IReadMapping interface for objects that already support the A.IReadWriteMapping interface.

At this point, if we supply an object that supports IReadWriteMapping, to a function that expects an IReadMapping, it should work, as long as we call adapt(ob,IReadMapping) (or IReadMapping(ob)) first, or the code we're calling does so.

There are still other ways to declare that one interface implies another. For example, if the author of our example package B knew about package A and its IReadWriteMapping interface, he or she might have defined IReadMapping this way:


\begin{verbatim}import protocols
from protocols import Interface
\par
from A imp...
...\par
def __getitem__(key):
''''''Return value for key''''''
\par
\end{verbatim}

This is syntax sugar for creating the interface first, and then using protocols.declareAdapter(NO_ADAPTER_NEEDED). Of course, you can only use this approach if you are the author of the interface! Otherwise, you must use declareAdapter() after the fact, as in the previous example.

In later sections, we will begin looking at the protocols declaration APIs - like declareAdapter() and advise() - in more detail. But first, we must look briefly at the interfaces that the protocols package expects from the protocols, adapters, and other objects supplied as parameters to the declaration API.