It bugs me that the default __repr__()
for a class is so uninformative:
>>> class Opaque(object): pass
...
>>> Opaque()
<__main__.Opaque object at 0x7f3ac50eba90>
... so I've been thinking about how to improve it. After a little consideration, I came up with this abstract base class which leverages the pickle
protocol's __getnewargs__()
method:
from abc import abstractmethod
class Repro(object):
"""Abstract base class for objects with informative ``repr()`` behaviour."""
@abstractmethod
def __getnewargs__(self):
raise NotImplementedError
def __repr__(self):
signature = ", ".join(repr(arg) for arg in self.__getnewargs__())
return "%s(%s)" % (self.__class__.__name__, signature)
Here's a trivial example of its usage:
class Transparent(Repro):
"""An example of a ``Repro`` subclass."""
def __init__(self, *args):
self.args = args
def __getnewargs__(self):
return self.args
... and the resulting repr()
behaviour:
>>> Transparent("an absurd signature", [1, 2, 3], str)
Transparent('an absurd signature', [1, 2, 3], <type 'str'>)
>>>
Now, I can see one reason Python doesn't do this by default straight away - requiring every class to define a __getnewargs__()
method would be more burdensome than expecting (but not requiring) that it defines a __repr__()
method.
What I'd like to know is: how dangerous and/or fragile is it? Off-hand, I can't think of anything that could go terribly wrong except that if a Repro
instance contained itself, you'd get infinite recursion ... but that's solveable, at the cost of making the code above uglier.
What else have I missed?
One problem with this whole idea is that there can be some kinds of objects who's state is not fully dependent on the arguments given to its constructor. For a trivial case, consider a class with random state:
import random
def A(object):
def __init__(self):
self.state = random.random()
There's no way for this class to correctly implement __getnewargs__
, and so your implantation of __repr__
is also impossible. It may be that a class like the one above is not well designed. But pickle
can handle it with no problems (I assume using the __reduce__
method inherited from object
, but my pickle-fu is not enough to say so with certainty).
This is why it is nice that __repr__
can be coded to do whatever you want. If you want the internal state to be visible, you can make your class's __repr__
do that. If the object should be opaque, you can do that too. For the class above, I'd probably implement __repr__
like this:
def __repr__(self):
return "<A object with state=%f>" % self.state