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:
The ``pure'' style emphasizes the interface as seen by the caller, and is not
intended to be subclassed for implementation. Notice that the
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:
IReadWriteMapping interface implies the
interface. Therefore, any object that supports
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
IReadMapping, because we
understand what they do. But there is no way for Python to know this, unless
we explicitly state it, like this:
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
At this point, if we supply an object that supports
to a function that expects an
IReadMapping, it should work, as long as
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
IReadWriteMapping interface, he or she might have defined
IReadMapping this way:
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.