- Interface: the collection of methods and attributes that a class or module exposes for use by the rest of the program
- Statically typed languages like Java define formal interfaces. The code will not compile if a class does not implement every method defined in its interface.
- Dynamically typed languages like Python, Ruby, and Javascript use Protocols, which are informal interfaces not enforced by the language, but followed by programmers as a best practice.
- Ex: Sequence Protocol
- Abstract base class for a sequence:
- has an abstract
__getitem__
method - implements concrete
__contains__
,__iter__
and__reversed__
methods
- has an abstract
- If a programmer implements their own sequence like object with only a
__getitem__
method, there are default implementations of the other 3 methods.
- Monkey Patching - changing a class or module at runtime, without modifying the source code.
- French Deck example:
FrenchDeck
class did not implement a shuffle method- Author monkey patches the
__setitem__
method by making a direct assignmentFrenchDeck.__setitem__ = set_card
__setitem__
is part of the mutable sequence protocol.- The
random.shuffle
method works on mutable sequences, so it can now be called onFrenchDeck
instances.
- Warning: Monkey Patched code is tightly coupled.
- Duck typing requires avoiding use of isinstance to check types.
- Allows for more flexible programs.
- Shortcoming of duck typing: unrelated objects may share a method name that behaves differently in each context.
- Ex:
Class Artist: def draw(self): ... Class Gunslinger: def draw(self): ...
- Goose typing - account for context by using abstract base classes
- okay to use isinstance, provided it is only used on ABCs.
- suggests not implementing custom ABCs, but making use of the ABCs that classes implicitly inherit from through protocols
- Inheriting from an ABC can be used to communicate intent and give context.
- Python defines ABCs for users to inherit from. These should cover a large percentage of use cases, and inheriting from an existing ABC is often a better choice than defining a new one.
- Exception: if you are writing a framework
- Implementations of ABCs are checked at runtime, and the interpreter raises a
TypeError
if a method of an abstract class is not implemented by the subclass.
- collections.abc module : python docs
- numbers module: python docs
- use for type checks. e.g.
isinstance(x, numbers.Integral)
instead ofisinstance(x, int)
where you want to includebool
,int
, and types from other libraries that have been registered as virtual subclasses ofnumbers.Integral
.
- use for type checks. e.g.
- Inherit from
abc.ABC
- use
@abstractmethod
decorator register
method: registers a virtual subclass of an abc. This will change the behavior ofisinstance
andissubclass
calls, but will not require the virtual subclass to implement abstract methods from the base class.
- Following established protocols improves the chances of reusing standard library and third party code.
- Try implementing existing ABCs from the standard library before creating your own.
- Implementing ABCs can give you default method implementations for free.