Last active
June 29, 2021 19:26
-
-
Save plammens/aa73bbf3e7e999a70d37e84a9ba9ef05 to your computer and use it in GitHub Desktop.
Generic __repr__ implementation based on the constructor's signature
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import inspect | |
class AutoReprEqMixin: | |
""" | |
Adds a generic __repr__ and __eq__ implementation. | |
The __repr__ implementation is based on the constructor's signature and relies | |
on the existence of attributes with the same name as the parameters they were | |
initialised from. | |
The __eq__ implementation is based on a generic equality check of the objects' | |
types and attributes. An __eq__ method is included in this same class because the | |
Python language dictates that __repr__ should try to return an expression that | |
when evaluated yields an object that is equal in value (==) to the original object. | |
If the subclass' semantics mean that these implementations no longer satisfy the | |
Python language's rules and conventions, it should be responsible for overriding | |
them with appropriate custom implementations. | |
""" | |
# this class uses double underscore attributes to avoid interfering with the | |
# actual class' namespace; this is a mixin and should be minimally intrusive | |
def __init__(self): | |
# cache signature | |
self.__signature = inspect.signature(type(self)) | |
def __repr__(self): | |
params = ", ".join( | |
self.__repr_param(name, param) | |
for name, param in self.__signature.parameters.items() | |
) | |
return f"{type(self).__name__}({params})" | |
def __repr_param(self, name: str, param: inspect.Parameter) -> str: | |
if param.kind == inspect.Parameter.POSITIONAL_ONLY: | |
return repr(getattr(self, name)) | |
elif param.kind == inspect.Parameter.VAR_POSITIONAL: | |
return ", ".join(map(repr, getattr(self, name))) | |
elif param.kind in ( | |
inspect.Parameter.POSITIONAL_OR_KEYWORD, | |
inspect.Parameter.KEYWORD_ONLY, | |
): | |
return f"{name}={getattr(self, name)!r}" | |
else: | |
raise NotImplementedError( | |
f"Automatic repr not implemented for parameter {name} of type {param.kind}" | |
) | |
def __eq__(self, other): | |
if not isinstance(other, AutoReprEqMixin): | |
return NotImplemented # delegate to other object | |
return type(self) is type(other) and vars(self) == vars(other) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment