I order to illustrate this feature, we’ll implement support for interface contracts for Python classes. In a language like Java, for instance, a contract can be declared this way:
And a class declared as an implementation of this interface must provide the relevant methods (with the correct signature). Otherwise, a compilation error arises. For instance:
We’ll try to achieve a similar feature in Python, albeit with some limitations. First, as Python does not have an explicit compilation step, our errors will only arise in runtime. Second, in Python, the types of the arguments and return values aren’t present in the signature of methods, so we’ll leave it out too. This is a notable absence, but we can still verify the presence of desired methods in the implementer type and the following elements in the method’s signature:
- Number of arguments
- Name of arguments (as Python supports named parameters)
- Default values for arguments (as Python supports default arguments)
- Support for variadic functions (indefinite number of arguments)
This said, we now have to declare our interfaces. I picked the following (somewhat cumbersome) syntax:
Here we have an ordinary class subtyping an
interface class. The methods are defined as attributes of the
some_interface class, holding instances of the
interface class isn’t really exciting or even necessary. Actually, it exists for the sole purpose of tagging an interface contract as such:
method class holds the method signature.
Notice that the constructor takes a few arguments:
- `args` — the list of arguments
- `varargs` — the name of the list holding extra arguments, if any
- `kwargs` — the name of the dictionary holding extra named arguments, if any
- `defaults` — the number of arguments with a default value
Now we can create a new type implementing
some_interface. And it follows:
Notice that we declare the class as implementing the contract through the
implements class decorator.
First thing we have to observe is the initialization of the
implements decorator. Once the decorated class is loaded by the interpreter, an instance of the decorator is produced. The argument
some_interface is passed to the constructor during the initialization (and it’s stored as an attribute for future reference).
__call__ method of the decorator is invoked. The decorated class (
some_class in this example) is passed as argument. In this method, it is verified whether the given class satisfies all of its contractual obligations. If no problem arises, the class is returned at the end of the call.
Notice that the
_assert_implements method is called against each method declared in the interface. This method, listed ahead, verifies whether the given method, as implemented by the class, has the desired signature, as specified in the interface. In case it doesn’t, an exception is raised (effectively interrupting the program).
object.__getattribute__() (documentation) call is one thing worth of note as it is part of Python’s introspection features and targeted at retrieving attributes from classes and objects. Here, it’s used both to retrieve
method objects from the interface and and to obtain references to the actual methods in the concrete class (the
getargspec function (documentation) is also an interesting introspection feature. Given a function, it will return an object with information about the given function’s signature.
So, having both the expected signature from the
method object hosted in the interface and the signature of the method implemented in the concrete class, we can simply compare them both and hope for a match.
(Postscript: although the source code presented above works fine, it’s actually a shorter version of the code originally developed for the purpose and available here.)