I can think of four ways to produce "identical" __str__
and __repr__
methods... not counting awfulness like copy & paste.
(For these examples, I'm using a stripped-down ClusterAssignment
class with a meaningless string representation and no other methods.)
You can use Tim Castelijns' method, with a single "private" _str
method, called by both __str__
and __repr__
:
class ClusterAssignment(object):
def _str(self):
return "[string]"
def __str__(self):
return self._str()
def __repr__(self):
return self._str()
This works, but requires repeating yourself in the bodies of the two functions.
To avoid the repetition, you can write one of __str__
or __repr__
normally, and then have the other method do nothing but call the "real" method. Which method you write "for real" depends on your intended output --- is it more "stringy" or more "repr-y"?
There are then two ways to "call the 'real' method": using the dunder-method (first example below), or using the public interface (second example). I suggest using the public interface, since that's what your users have to do.
class ClusterAssignment(object):
def __str__(self):
return "[string]"
def __repr__(self):
# Uses "private" dunder-method.
return self.__str__()
class ClusterAssignment(object):
def __str__(self):
return "[string]"
def __repr__(self):
# Uses public interface.
return str(self)
Finally, you can give the one "real" method an alias:
class ClusterAssignment(object):
def __str__(self):
return "[string]"
__repr__ = __str__
No repetitions here, and no redundant method definition: __repr__
is __str__
.
I think this is explicit (better than implicit) --- there is only one task performed, so there is only one method. Others might see this as the opposite --- there are now two names masquerading as two methods, when there is really only one. Also, their order in the source code now matters --- __repr__
must be defined after __str__
.
One quirk of the aliasing technique: The string representations of the methods themselves are now identical too. The first three versions produce:
>>> ca = ClusterAssignment()
>>> print(ca.__str__)
<bound method ClusterAssignment.__str__ of [string]>
>>> print(ca.__repr__)
<bound method ClusterAssignment.__repr__ of [string]>
The aliased version makes it clear that there is only one method:
>>> ca = ClusterAssignment()
>>> print(ca.__str__)
<bound method ClusterAssignment.__str__ of [string]>
>>> print(ca.__repr__)
<bound method ClusterAssignment.__str__ of [string]>
...or it sends your co-workers hunting for the bugged-up line that's calling __str__
when it should be calling __repr__
.
This kind of decision is what project style guides are for.