Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to overwrite __repr__ method for an already-instantiated class python

I'm working with a 3rd-party library which has a poor repr for a class and I would like to overwrite it once an instance of that class has been created.

I saw how to create bound methods in an existing object.

class toy():
    pass

inst= toy()

class inhtoy():
    def __new__(cls,obj):
        def __repr__(self):
            return 'Sucessful'
        import types
        obj.__repr__ = types.MethodType(__repr__,obj)
        return obj

t = inhtoy(inst)

Indeed, if I call t.repr() it works, however it does not overwrite the original repr. It appears as <bound method inhtoy.__new__.<locals>.__repr__ of <__main__.toy object at 0x7f76e0b61f98>> a kind of local method.
Calling repr(t) still points to the original representation '<__main__.toy object at 0x7f76e0b61f98>' but not the overwritten one.

Is there a way to properly do this?
Thanks

like image 591
Juan Felipe Avatar asked Apr 30 '19 10:04

Juan Felipe


2 Answers

@Nullman's answer works, because their solution is actually changing the class object toy, that t is an instance of, not the instance itself, as your approach does.

The special attribute __class__ references the class object an instance belongs to.

print(t.__class__ is toy) # True

So, t.__class__.__repr__ = my_custom_repr assigns to __repr__ on the class toy, not on the instance t.

This becomes visible when comparing the output of print(t.__repr__) between your approach and Nullman's. Assuming that a module-level function __repr__ looks like this:

def __repr__(self):
    return repr(self.__class__)

Your solution shows:

<bound method __repr__ of <__main__.toy object at 0x00000000029E5A90>>

Note, it says __main__.toy object.
Nullman's solution shows it as:

<bound method __repr__ of <class '__main__.toy'>>

When you invoke t.__repr__() using your approach, you call the method you set on the instance t, hence it returns what you made it retun; the string Success in your example.
When using repr(), however, the class defines the output:

A class can control what this function returns for its instances by defining a __repr__() method.

As Nullman rightfully pointed out, their approach will change the behavior of all existing and future objects, instantiated from toy.


As for the strange name, that the assigned method shows when using your code:

<bound method inhtoy.__new__.<locals>.__repr__ of <__main__.toy object at 0x7f76e0b61f98>>

... that's the function object's qualified name coming from the __qualname__ special attribute. It's the function __repr__ from the local scope of your class inhtoy's method __new__.
Speaking of which, passing your instance inst through the magic method __new__ of your inhtoy class does not really achieve much. Your code is functionally equivalent to:

def __repr__(self):
    return "Success"

inst.__repr__ = types.MethodType(__repr__, obj)
like image 126
shmee Avatar answered Nov 02 '22 17:11

shmee


after some looking around i found this answer. the way to do it is on a live instance is:

t.__class__.__repr__ = my_custom_repr

please note that this changes all class instances and not just this instance

like image 41
Nullman Avatar answered Nov 02 '22 16:11

Nullman