8

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

Juan Felipe
  • 155
  • 1
  • 5
  • As bad as their `__repr__` may be, replacing it with a substitute of your own is going to be even more confusing. – user2357112 Apr 30 '19 at 10:20
  • while i would also like to know how to do this to a live instance, why not subclass their class and just changing the `__repr__`? – Nullman Apr 30 '19 at 10:31
  • @Nullman Because subclassing would require the user to inherit it. It would also change class name generating incompatibilities with 3rd-party library. Modifiying the object keeps everything the same. – Juan Felipe Apr 30 '19 at 10:41

4 Answers4

3

@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)
shmee
  • 4,721
  • 2
  • 18
  • 27
1

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

Nullman
  • 4,179
  • 2
  • 14
  • 30
  • Well, for my purpose it will work. Do you have any insight about why it works that way? – Juan Felipe Apr 30 '19 at 10:52
  • unfortunately not, but if you figure it out i would be happy if you let me know – Nullman Apr 30 '19 at 10:54
  • 1
    @Nullman I added an answer in an attempt to explain why your solutions works and the OP's doesnt; in case you are still interested ... – shmee May 02 '19 at 08:09
1

You can use inheritance to create a new class derived from the class of interest, and then overriding the repr method

class inhtoy(toy):

    def __init__(self):
        toy.__init__(self)

    def __repr__(self):
        # Your repr method
Alexis Pister
  • 449
  • 3
  • 13
1

Use a subclass and assign it to the instance, which you get from the library like this:

class Toy():
    pass
inst = Toy()  # from 3rd party library

class Toy_MyRepr(Toy):
    def __repr__(self):
        return '<my repr of Toy instance >'

inst.__class__ = Toy_MyRepr
kxr
  • 4,841
  • 1
  • 49
  • 32