Skip to content

Instantly share code, notes, and snippets.

@plammens
Last active June 29, 2021 19:26
Show Gist options
  • Save plammens/aa73bbf3e7e999a70d37e84a9ba9ef05 to your computer and use it in GitHub Desktop.
Save plammens/aa73bbf3e7e999a70d37e84a9ba9ef05 to your computer and use it in GitHub Desktop.
Generic __repr__ implementation based on the constructor's signature
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