How can we get the default behavior of __repr__()?
Asked Answered
A

3

23

If someone writes a class in python, and fails to specify their own __repr__() method, then a default one is provided for them. However, suppose we want to write a function which has the same, or similar, behavior to the default __repr__(). However, we want this function to have the behavior of the default __repr__() method even if the actual __repr__() for the class was overloaded. That is, suppose we want to write a function which has the same behavior as a default __repr__() regardless of whether someone overloaded the __repr__() method or not. How might we do it?

class DemoClass:
    def __init__(self):
        self.var = 4
    def __repr__(self):
        return str(self.var)

def true_repr(x):
    # [magic happens here]
    s = "I'm not implemented yet"
    return s

obj = DemoClass()

print(obj.__repr__())

print(true_repr(obj))

Desired Output:

print(obj.__repr__()) prints 4, but print(true_repr(obj)) prints something like:
<__main__.DemoClass object at 0x0000000009F26588>

Argive answered 13/2, 2018 at 22:52 Comment(0)
S
29

You can use object.__repr__(obj). This works because the default repr behavior is defined in object.__repr__.

Sacrosanct answered 13/2, 2018 at 22:55 Comment(0)
L
20

Note, the best answer is probably just to use object.__repr__ directly, as the others have pointed out. But one could implement that same functionality roughly as:

>>> def true_repr(x):
...     type_ = type(x)
...     module = type_.__module__
...     qualname = type_.__qualname__
...     return f"<{module}.{qualname} object at {hex(id(x))}>"
...

So....

>>> A()
hahahahaha
>>> true_repr(A())
'<__main__.A object at 0x106549208>'
>>>
Longevous answered 13/2, 2018 at 22:58 Comment(2)
Nifty! Didn't know about __qualname__, is it a recent addition?Swound
@cᴏʟᴅsᴘᴇᴇᴅ: Relatively recent; __qualname__ was added in version 3.3: legacy.python.org/dev/peps/pep-3155Beachhead
T
10

Typically we can use object.__repr__ for that, but this will to the "object repr for every item, so:

>>> object.__repr__(4)
'<int object at 0xa6dd20>'

Since an int is an object, but with the __repr__ overriden.

If you want to go up one level of overwriting, we can use super(..):

>>> super(type(4), 4).__repr__()  # going up one level
'<int object at 0xa6dd20>'

For an int that thus again means that we will print <int object at ...>, but if we would for instance subclass the int, then it would use the __repr__ of int again, like:

class special_int(int):

    def __repr__(self):
        return 'Special int'

Then it will look like:

>>> s = special_int(4)
>>> super(type(s), s).__repr__()
'4'

What we here do is creating a proxy object with super(..). Super will walk the method resolution order (MRO) of the object and will try to find the first function (from a superclass of s) that has overriden the function. If we use single inheritance, that is the closest parent that overrides the function, but if it there is some multiple inheritance involved, then this is more tricky. We thus select the __repr__ of that parent, and call that function.

This is also a rather weird application of super since usually the class (here type(s)) is a fixed one, and does not depend on the type of s itself, since otherwise multiple such super(..) calls would result in an infinite loop.

But usually it is a bad idea to break overriding anyway. The reason a programmer overrides a function is to change the behavior. Not respecting this can of course sometimes result into some useful functions, but frequently it will result in the fact that the code contracts are no longer satisfied. For example if a programmer overrides __eq__, he/she will also override __hash__, if you use the hash of another class, and the real __eq__, then things will start breaking.

Calling magic function directly is also frequently seen as an antipattern, so you better avoid that as well.

Theorbo answered 13/2, 2018 at 22:55 Comment(1)
The issue here is that the intuitive concept of "the default __repr__" may not really mean what the questioner wants it to mean. (Also, I would normally downvote anything that suggests passing the output of type to super, but it may make sense here. It needs more warnings about grandchild classes, though.)Damselfly

© 2022 - 2024 — McMap. All rights reserved.