Python decorators are quite useful and interesting. I’ve already written about function decorators in a previous post and class decorators are a worthy followup.
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:
- Name
- 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 method
class.
The interface
class isn’t really exciting or even necessary. Actually, it exists for the sole purpose of tagging an interface contract as such:
The 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).
Then, the __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).
The 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 clazz
argument).
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.)