I am currently learning Python (from Java) and have a question on contracts.
Example: an application defines an interface that all plugins must implement and then the main application can call it.
In Java:
public interface IPlugin {
public Image modify(Image img);
}
public class MainApp {
public main_app_logic() {
String pluginName = "com.example.myplugin";
IPlugin x = (IPlugin) Class.forName(pluginName);
x.modify(someimg);
}
}
The plugin implements the interface and we use reflection in main app to call it. That way, there’s a contract between the main app and the plugin that both can refer to.
How does one go about doing something similar in Python? And also, which approach is better?
P.S. I’m not posting this on SO because I’m much more concerned with the philosophy behind the two approaches.
2
Python’s approach to this problem is is about as opposite as you can get to Java’s approach.
In Python there are no contracts, restrictions, agreements, or requirements with respect to objects and their capabilities. An object is assumed to have any given method or property until you actually try to use it. If it works then it works, but if the attribute doesn’t exist, then it’s a runtime error. You can either try to catch that AttributeError
before it blows up your program, or you can debug from the resultant stack trace.
If the method exists but returns the wrong type of result, then hopefully either you’ll get an exception somewhere down the road that you can catch, or perhaps you’ll get some sort of output sometime later that you’ll alert you to your mistake.
As mentioned, you can use the abc module to define an Abstract Base Class
and register your class as an implementation. This will throw a TypeError
when you instantiate your class if it doesn’t implement all of the abstract methods described in your abstract base class. There’s obviously no way to check return types, though, since Python doesn’t really have a concept of return types.
This really underscores the need for 100% code coverage on your unit tests for Python code. These mistakes can lay quietly undetected unless you exercise every piece of code under every allowable set of circumstances.
1
Python, being a dynamic programming language, does not usually require the rigidity of Java contracts.
If you are looking for a plugin-style registry, the python distribution packaging library setuptools
provides methods to register “entry points”; using the pkg_resources
module (part of the setuptools
project) you can then enumerate everything registered for a given entry point, and different packages can register their plugins that way. See Explain Python entry points.
Python does provide ‘contracts’ of a sort, called Abstract Base Classes. Their purpose is more to test if an object can satisfy the contract, and is not normally used for discovery.
Last but not least, the add-on packages zope.interface
and zope.component
(part of the Zope Component Architecture) provide both a method to specify contracts, and the means to look up components registered as implementing a given contract.