Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to override a class' __call__ method? [duplicate]

I need to change the behavior of the __call__ method of a given object. The naive approach would be something like:

class A(object):
    def __call__(self):
        return 1

def new_call():
    return 42


a = A()
a.__call__ = new_call

Why is it not the output of a() 42? Is there a workaround I can exploit to achieve the same effect? (without using the class)


============================ EDIT =================================

For the records, the short answer is no. Python calls the "special methods" like __call_ directly on the class and not on the instance, therefore if you need to change the method, you need to change it on the class itself.

like image 776
Andrea Avatar asked Sep 18 '25 00:09

Andrea


1 Answers

Special methods (aka "dunder" methods) are looked-up with respect to the class of the object, so to override it you would need to change the class, not the instance. Also note that methods all have an initial argument passed to them, usually called self.

The following would do what you want (notice how that it affects all instances of the class):

class A(object):
    def __call__(self):
        return 1

def new_call(self):
    return 42


a1 = A()
a2 = A()
A.__call__ = new_call

print(a1())  # -> 42
print(a2())  # -> 42

If you only want to change a specific instance, a relatively simple workaround is to make the class' __call_() method call one that's not "special" like it is — i.e. by introducing a level of indirection.

Here's what I mean:

# Workaround

class B(object):
    def __call__(self):
        return self.call_method(self)

    @staticmethod
    def call_method(self):
        return 1

def new_call(self):
    return 42

# Create two instances for testing.
b1 = B()
b2 = B()
b2.call_method = new_call  # Will only affect this instance.

print(b1())  # -> 1
print(b2())  # -> 42
like image 77
martineau Avatar answered Sep 20 '25 15:09

martineau