0
votes

I would like to override the method of an instance of class A with a method from class B but in a way so that all references to the old method of the instance (made before overriding the method) then 'link' to the new one. In code:

import types

class A:
    def foo(self):
        print('A')

class B:
    def foo(self):
        print('B')

class C:
    def __init__(self, a):
        self.func = a.foo

    def do_something(self):
        self.func()


a = A()
c = C(a)


method_name = 'foo'  # it has to be dynamic
new_method = getattr(B, method_name)
setattr(a, method_name, types.MethodType(new_method, a))


c.do_something()  # still prints A, I want it to print B now

I want c.func to hold the new method from class B after the attribute of a has been set (without doing anything with the c object).

Is there a way to set the attribute of the instance a so that all previously made references then refer to the new method?

Sorry if this question is kind of stupid, I am not that much into this.

2
Note that for this to work, c must know a, not just a.foo. Is that an acceptable change from your setup?MisterMiyagi
Not always. But in some cases, it might help. It depends on the implementation.mep

2 Answers

2
votes

You could do it like this, for example:

...
def retain(foo):
    return lambda *args, **kwargs: getattr(foo.__self__, foo.__name__)(*args, **kwargs)

class C:
    def __init__(self, a):
        self.func = retain(a.foo)
...
0
votes

Just adding to Alex's answer. In my case, the described dynamic partly comes from a need for serialization and deserialization. To serialize certain method references, I used to use func.__name__.

However, c.func.__name__ would only return <lambda> using Alex's approach. I prevented this by creating a callable class that uses the retain function but stores the method's name separately which in my case is enough because it's just some specific references I need to serialize.

def retain(foo):
    return lambda *args, **kwargs: getattr(foo.__self__, foo.__name__)(*args, **kwargs)

class M:
    def __init__(self, method):
        self.method_name = method.__name__
        self.method = retain(method)

    def __call__(self, *args, **kwargs):
        self.method(*args, **kwargs)

class A:
    def foo(self):
        print('A')

class B:
    def foo(self):
        print('B')

class C:
    def __init__(self, method):
        self.func = M(method)

    def do_something(self):
        self.func()

a = A()
c = C(a.foo)
setattr(a, method_name, types.MethodType(getattr(B, 'foo'), a))
c.do_something()  # now prints B

# when serializing
method_name = c.func.method_name