Given the following code:
class Base:
__slots__ = 'base_field',
def __init__(self, base_field):
self.base_field = base_field
def __repr__(self):
return f"{self.__class__.__name__}(base_field={self.base_field!r})"
class Derived(Base):
__slots__ = 'derived_field',
def __init__(self, derived_field, **kwargs):
super().__init__(**kwargs)
self.derived_field = derived_field
def __repr__(self):
return f"{self.__class__.__name__}(base_field={self.base_field!r}, derived_field={self.derived_field!r})"
b1 = Base('base')
print(repr(b1))
b2 = eval(repr(b1))
d1 = Derived(base_field='dbase', derived_field='derived')
print(repr(d1))
d2 = eval(repr(d1))
This code will break if I add a new field to Base
and forget to update the Derived
class' __repr__
.
How and where should I define a __repr__
method(s) such that:
- When I pass both
Base
andDerived
instances torepr()
, their correct strings will be returned (e.g.repr(Base("base")) == "Base(base_field="base")
andrepr(Derived(base_field='dbase', derived_field='derived')) == "Derived(base_field="base", derived_field="derived")
- I can safely add fields to the
Base
class and those fields will show up in in theDerived
class'__repr__
output. In other words, theDerived
class'__repr__
function should not know about the fields in theBase
class. - The code is minimal. If I have to add more than a couple of lines, the extra code might not be worth it
I'm reading some code where the author has solved the problem by making the Derived
class __repr__
call super().__repr__()
and then remove the last parenthesis and append the Derived
attributes to the string. This does the job but I'm wondering if there is a more Pythonic way of achieving this goal. Or, perhaps this is the Pythonic way?
I looked into Python's dataclass
and that does a great job of building __repr__
s that do the right thing. However they use __dict__
s so there are trade-offs to consider (e.g. smaller memory foot print for an object that has many instances at once, vs. easier __repr__
).
Thank you!