Package: abstracts
Abstract class and interface definitions.Create anabstract.AbstractionAnAbstractionis ametaclassfor defining abstract classes.Let’s define an abstractAFooclass and give it an abstractdo_foomethod.Like any python class, anAbstractioncan have any name, but it may
be helpful to distinguish abstract classes from others by prefixing their
name withA.>>>importabc>>>importabstracts>>>classAFoo(metaclass=abstracts.Abstraction):......@abc.abstractmethod...defdo_foo(self):...raiseNotImplementedErrorAbstract classescannotbe instantiated directly.>>>AFoo()Traceback (most recent call last):...TypeError:Can't instantiate abstract class AFoo with abstract method... do_fooCreate animplementerfor anabstract.AbstractionIn order to make use ofAFoo, we need to create an implementer for it.>>>@abstracts.implementer(AFoo)...classFoo:...passThe implementermustimplement all of the abstract methods,
defined by its abstract classes.>>>Foo()Traceback (most recent call last):...TypeError:Can't instantiate abstract class Foo with abstract method... do_foo>>>@abstracts.implementer(AFoo)...classFoo2:......defdo_foo(self):...return"DID FOO">>>Foo2()<__main__.Foo2 object at ...>An implementer inherits from itsAbstractionsAnimplementerclass is a subclass of itsAbstraction.>>>issubclass(Foo2,AFoo)TrueLikewise an instance of an implementer is an instance of itsAbstraction>>>isinstance(Foo2(),AFoo)TrueTheAbstractionclass can be seen in the classbases, and the
methods of theAbstractioncan be invoked by the implementer.>>>importinspect>>>AFooininspect.getmro(Foo2)TrueCreate animplementerthat implements multipleAbstractions.An implementer can implement multiple abstractions.Let’s create a second abstraction.>>>classABar(metaclass=abstracts.Abstraction):......@abc.abstractmethod...defdo_bar(self):...raiseNotImplementedErrorAnd now we can create an implementer that implememts both theAFooandABarAbstractions.>>>@abstracts.implementer((AFoo,ABar))...classFooBar:......defdo_foo(self):...return"DID FOO"......defdo_bar(self):...return"DID BAR">>>FooBar()<__main__.FooBar object at ...>Defining abstract propertiesProperties can be defined in an abstract class, and just like with normal
methods, they must be implemented by any implementers.>>>classAMover(metaclass=abstracts.Abstraction):......@property...@abc.abstractmethod...defspeed(self):...return5......@property...@abc.abstractmethod...defdirection(self):...return"forwards"Callingsuper()on anabstractmethodJust like with pythons “Abstract Base Classes” you can callsuper()in anabstractmethod, to invoke an abstract implementation.>>>@abstracts.implementer(AMover)...classMover:......@property...defdirection(self):...return"backwards"......@property...defspeed(self):...returnsuper().speedThis custom implementation ofAMovermustimplement bothspeedanddirection, even if its implementation invokes the abstract implementation.In this case it uses the default/abstract implementation ofspeedwhile providing
its own implementation ofdirection.>>>mover=Mover()>>>mover<__main__.Mover object at ...>>>>mover.speed5>>>mover.direction'backwards'Defining anabstracts.InterfaceclassAnInterfaceis much like anAbstraction, but with a few differences.AnInterfacecan only define methods with the@interfacemethoddecorator.It cannot define normal methods or methods with the@abstractmethod, only methods
with@interfacemethod.An@interfacemethodif invoked will always raise anNotImplementedError, and
therefore cannot be used as an abstract implementation.Lets add anInterfaceclass that we can use.In the way that it may be helpful to distinguish anAbstractionfrom other
types of classes, it may be also useful to distinguish anInterfaceby
using anIprefix when naming them.>>>classIGeared(metaclass=abstracts.Interface):......@property...@abstracts.interfacemethod...defnumber_of_gears(self):...# Raising an error is ~superfluous as the decorator will raise...# anyway if the method is invoked....raiseNotImplementedErrorImplementing anInterfaceJust like with anAbstraction, anInterfacecan be implemented using
the@implementerdecorator.An implementer, can implement a combination ofAbstractionsandInterfaces.>>>@abstracts.implementer((AMover,IGeared))...classBicycle:......@property...defdirection(self):...returnsuper().direction......@property...defspeed(self):...returnsuper().speed......@property...defnumber_of_gears(self):...return7>>>Bicycle().number_of_gears7An implementer doesnotinherit from itsInterfacesAnimplementerclass is a subclass of itsInterfaces.>>>issubclass(Bicycle,AMover)True>>>issubclass(Bicycle,IGeared)TrueLikewise an instance of an implementer is an instance of itsInterfaces>>>isinstance(Bicycle(),AMover)True>>>isinstance(Bicycle(),IGeared)TrueUnlike withAbstractionsit doesnothowever, inherit from itsInterfaces.>>>AMoverininspect.getmro(Bicycle)True>>>IGearedininspect.getmro(Bicycle)False@interfacemethodscan never be invokedThe key thing to remember is that you cannot callsuper()on any@interfacemethod, or directly invoke it.If it was defined as part of anInterfaceyou will receive anAttributeError, as the implementation does not inherit directly from the
interface.>>>@abstracts.implementer((AMover,IGeared))...classBrokenBicycle:......@property...defdirection(self):...returnsuper().direction......@property...defspeed(self):...returnsuper().speed......@property...defnumber_of_gears(self):...returnsuper().number_of_gears>>>BrokenBicycle().number_of_gearsTraceback (most recent call last):...AttributeError:'super' object has no attribute 'number_of_gears'WarningMisuse of this class can haveunintended consequencesIf you invokesuper()on an@interfacemethoddefined as part of anAbstractionit will raiseNotImplementedError.As anInterfacecan only hold this type of method, you can never invoke
any of its methods. Doing so directly will raising aNotImplementedError.>>>IGeared.number_of_gears.__get__(Bicycle())Traceback (most recent call last):...NotImplementedErrorCombining@abstractmethodand@interfacemethodin anAbstractionAsInterfacesare “pure”, they cannot use@abstractmethodor contain any implementation.AnAbstractionon the other hand can combine both.Lets create a pureInterfacethat represents a “shed”.>>>classIShed(metaclass=abstracts.Interface):......@property...@abstracts.interfacemethod...defsize(self):...raiseNotImplementedErrorWe can use this interface to create anABikeShedAbstraction>>>classABikeShed(IShed,metaclass=abstracts.Abstraction):......@property...@abstracts.interfacemethod...defmax_bike_size(self):...raiseNotImplementedError......@abc.abstractmethod...defget_capacity(self):...returnint(self.size/self.max_bike_size)We can now create an implementation.It will need to define both thesizeand themax_bike_size,
as these areinterfacemethods.It can, however, make use of the abstract implementation ofget_capacity,
even if it must be defined.>>>@abstracts.implementer(ABikeShed)...classBikeShed:......@property...defmax_bike_size(self):...return7......@property...defsize(self):...return161......defget_capacity(self):...returnsuper().get_capacity()>>>bikeshed=BikeShed()>>>bikeshed.get_capacity()23