How do I implement interfaces in python? How do I implement interfaces in python? python python

How do I implement interfaces in python?


As mentioned by other here:

Interfaces are not necessary in Python. This is because Python has proper multiple inheritance, and also ducktyping, which means that the places where you must have interfaces in Java, you don't have to have them in Python.

That said, there are still several uses for interfaces. Some of them are covered by Pythons Abstract Base Classes, introduced in Python 2.6. They are useful, if you want to make base classes that cannot be instantiated, but provide a specific interface or part of an implementation.

Another usage is if you somehow want to specify that an object implements a specific interface, and you can use ABC's for that too by subclassing from them. Another way is zope.interface, a module that is a part of the Zope Component Architecture, a really awesomely cool component framework. Here you don't subclass from the interfaces, but instead mark classes (or even instances) as implementing an interface. This can also be used to look up components from a component registry. Supercool!


Using the abc module for abstract base classes seems to do the trick.

from abc import ABCMeta, abstractmethodclass IInterface:    __metaclass__ = ABCMeta    @classmethod    def version(self): return "1.0"    @abstractmethod    def show(self): raise NotImplementedErrorclass MyServer(IInterface):    def show(self):        print 'Hello, World 2!'class MyBadServer(object):    def show(self):        print 'Damn you, world!'class MyClient(object):    def __init__(self, server):        if not isinstance(server, IInterface): raise Exception('Bad interface')        if not IInterface.version() == '1.0': raise Exception('Bad revision')        self._server = server    def client_show(self):        self._server.show()# This call will fail with an exceptiontry:    x = MyClient(MyBadServer)except Exception as exc:    print 'Failed as it should!'# This will pass with gloryMyClient(MyServer()).client_show()


Implementing interfaces with abstract base classes is much simpler in modern Python 3 and they serve a purpose as an interface contract for plug-in extensions.

Create the interface/abstract base class:

from abc import ABC, abstractmethodclass AccountingSystem(ABC):    @abstractmethod    def create_purchase_invoice(self, purchase):        pass    @abstractmethod    def create_sale_invoice(self, sale):        log.debug('Creating sale invoice', sale)

Create a normal subclass and override all abstract methods:

class GizmoAccountingSystem(AccountingSystem):    def create_purchase_invoice(self, purchase):        submit_to_gizmo_purchase_service(purchase)    def create_sale_invoice(self, sale):        super().create_sale_invoice(sale)        submit_to_gizmo_sale_service(sale)

You can optionally have common implementation in the abstract methods as in create_sale_invoice(), calling it with super() explicitly in the subclass as above.

Instantiation of a subclass that does not implement all the abstract methods fails:

class IncompleteAccountingSystem(AccountingSystem):    pass>>> accounting = IncompleteAccountingSystem()Traceback (most recent call last):  File "<stdin>", line 1, in <module>TypeError: Can't instantiate abstract class IncompleteAccountingSystem with abstract methodscreate_purchase_invoice, create_sale_invoice

You can also have abstract properties, static and class methods by combining corresponding annotations with @abstractmethod.

Abstract base classes are great for implementing plugin-based systems. All imported subclasses of a class are accessible via __subclasses__(), so if you load all classes from a plugin directory with importlib.import_module() and if they subclass the base class, you have direct access to them via __subclasses__() and you can be sure that the interface contract is enforced for all of them during instantiation.

Here's the plugin loading implementation for the AccountingSystem example above:

...from importlib import import_moduleclass AccountingSystem(ABC):    ...    _instance = None    @classmethod    def instance(cls):        if not cls._instance:            module_name = settings.ACCOUNTING_SYSTEM_MODULE_NAME            import_module(module_name)            subclasses = cls.__subclasses__()            if len(subclasses) > 1:                raise InvalidAccountingSystemError('More than one '                        f'accounting module: {subclasses}')            if not subclasses or module_name not in str(subclasses[0]):                raise InvalidAccountingSystemError('Accounting module '                        f'{module_name} does not exist or does not '                        'subclass AccountingSystem')            cls._instance = subclasses[0]()        return cls._instance

Then you can access the accounting system plugin object through the AccountingSystem class:

>>> accountingsystem = AccountingSystem.instance()

(Inspired by this PyMOTW-3 post.)


matomo