One of the ideas that I really like from Go is implicit interfaces. It allows clients to document the exact usage of a wide object and constrain what parts to be used. The good news is that Python’s optional type system allows for implicit interfaces.
Example
The pygame.time.Clock object has many methods and let’s say we want to document and constrain that we only use the tick
method. If we keep the signature the same as the implementation, there is no need to cast and type checking will make sure we use no other methods.
class SimClock(Protocol):
def tick(self, framerate: float) -> int:
"""
Wait for a tick to pass as defined by framerate. This is to keep the simulation running at a consistent rate.
"""
...
# Other code ...
= SimClock, pygame.time.Clock()
clock: SimClock while True:
# Processing code...
# this is good
clock.tick(fps) # clock.get_time() <- This will fail type checking
If the method signature is not the same, then we must cast. The example below changes tick
to accept an integer instead of a float and not caring about the return. Thus, being more restrictive. I feel casting is a typing code smell, so I stick with the signatures in the implementation. But, this can not be always avoided. As always there are no hard and fast rules.
class SimClock(Protocol):
def tick(self, framerate: int) -> None:
"""
Wait for a tick to pass as defined by framerate. This is to keep the simulation running at a consistent rate.
"""
...
# Other code ...
= cast(SimClock, pygame.time.Clock()) # CAST!
clock: SimClock while True:
# Processing code...
# same as before clock.tick(fps)
Why?
Why go through the trouble? We are tring to increase readability and help us focus on what the code is actually implementing. Reducing the surface area of an external object, helps with all of that. It means that someone new to the code doesn’t have to read through all of the methods in the library to know exactly which ones will be used in a glance. You can even add comments if none are available in the library to further reduce cognitive load.