Python interfaces

See Real Python

Informal interfaces

  • define a base class with ‘abstract" methods (e.g. throwing NotImplementedError)
  • Subclass by implementations
  • Pb : issubclass(<subclass>, <baseclass>) is always true even when the subclass doesn’t fully implement the interface
  • <subclass>.__mro__ shows the interface even when its not implemented
  • NB : when raising NotImplementedError, pycharm warns in the subclass but mypy doesn’t

Using Metaclasses

Check this great article

  • Implementing 2 dunder methods : __instancecheck__() and __subclasscheck__()
image-20220401152803914
  • By using a metaclass, you don’t need to explicitly define the subclasses. Instead, the subclass must define the required methods. If it doesn’t, then issubclass(EmlParserNew, UpdatedInformalParserInterface) will return False
  • As you can see, UpdatedInformalParserInterface is a superclass of PdfParserNew, but it doesn’t appear in the MRO. This unusual behavior is caused by the fact that UpdatedInformalParserInterface is a virtual base class of PdfParserNew.

Virtual base classes

  • When a class builds from a metaclass it will be a virtual base class of any class implementing the metaclass

  • A class building from a metaclass can be called an interface

  • We have

    • isinstance(Interface, Metaclass) == True if interface builds from the metaclass

    • issubclass(ConcreteClass, Interface) == True if ConcreteClass implements Interface

    • isinstance(ConcreteClass(), Interface) == True

image-20220401175101399

Metaclasses usage

  • See stack
  • Registering each subclass in a data structure
  • Allow changing the evaluation of class body
  • Allow creating overloaded methods

Formal Interfaces

Using abc.ABCMeta

  • like creating a custom metaclass but more standard
image-20220401180949945

Using abc to Register a Virtual Subclass

  • Once you’ve imported the abc module, you can directly register a virtual subclass by using the .register() metamethod
  • NB : allows creating metaclass of literal types for example
  • Once you’ve registered Double, you can use it as class decorator to set the decorated class as a virtual subclass. NB : can be dangerous
  • __subclasshook__ takes precedence over register

Using Abstract Method Declaration

Mypy Protocols

  • Mypy supports two ways of deciding whether two classes are compatible as types: nominal subtyping and structural subtyping
  • Nominal subtyping = normal inheritance, compatible with instance of
  • Structural subtyping :
    • Class D is a structural subtype of class C if the former has all attributes and methods of the latter, and with compatible types
    • Structural subtyping can be seen as a static equivalent of duck typing

Existing protocols

  • Iteration protocols: Iterable[T] and Iterator[T]
  • Collection protocols: Sized, Container[T], Collection[T]
  • etc.. (One-off protocols, Async Protocols, Context manager protocols ..)

User defined protocols

image-20220401155546245

  • Note that inheriting from an existing protocol does not automatically turn the subclass into a protocol – it just creates a regular (non-protocol) class or ABC that implements the given protocol (or protocols). The Protocol base class must always be explicitly present if you are defining a protocol

Interface libraries

  • zope.interface [zope-interfaces] was one of the first widely used approaches to structural subtyping in Python
  • Design by contract, supports invariants
image-20220408172725336