The video does not appear to be available as of December 2016, but I agree with you that interface inheritance is not as problematic as implementation inheritance. See https://softwareengineering.stackexchange.com/a/260354 for example.
However, one sense in which the
zope.interface "provider" relationship frees us from the inheritance hierarchy is that it allows objects to provide an interface even if their class does not. https://docs.zope.org/zope.interface/README.html#declaring-provided-interfaces shows examples of objects directly providing an interface although their respective classes do not.
For example, if an
IWriter interface requires
flush methods, then a module that defines top-level
flush functions (and calls
zope.interface.moduleProvides(IWriter)) provides that interface, even though modules in general do not.
This is also the reason that Zope interface methods do not name
self as a parameter -
self is an implementation detail particular to class instance methods.
This ability for individual objects to provide an interface becomes particularly useful with objects that wrap another object and dynamically map its methods.
For example, say we have:
def write(self, s):
tw = TextWriter()
If we see a problem where
tw output is not being flushed sometimes, we could instrument the TextWriter methods to display debug info (e.g.
log(timestamp, caller, methodname)). However, the code to generate the
caller info is complex, and needs to be added in multiple places. And now every
TextWriter instance will display the debug info.
Instead, we could wrap the particular
TextWriter of interest in a wrapper object that logs its method calls.
def __init__(self, wrapped):
self._wrapped = wrapped
def __getattr__(self, name):
... # generate timestamp and caller
log(timestamp, caller, name)
return getattr(self._wrapped, name)
dtw = DebugWrapper(tw)
DebugWrapper is generic. It can wrap any object. It could be imported from a separate library. It's independent of the
IWriter interface, and that is good since we can more easily re-use it elsewhere.
But if the
do_something function expects to receive a provider of the interface
IWriter we now have the problem that
dtw satisfies the interface syntax but does not "officially" provide the interface.
This is an important distinction.
Interfaces do not just define a syntactic signature. They also define the semantic meaning of the syntax. With interfaces we can specify whether two objects that provide the same methods actually provide the same semantics. This is a big difference to Python's common use of duck typing, where the same syntax implies the same semantics.
Anyway, the solution to our problem is to inform everyone that the object
dtw does indeed provide the
IWriter interface, even though its class
DebugWrapper does not.