0

I was under the impression that any object in python which as a __call__ attribute is callable, but this doesn't appear to be the case. In particular, setting __call__ in __init__ doesn't appear to be supported - see below. What exactly is required for an object to be callable and why doesn't this way work?

class A:
    def __init__(self):
        self.__call__ = self.double

    def double(self, x):
        """Doubles `x`."""
        return 2 * x

a = A()
try:
    print(a(3))
except TypeError as e:
    print(e) # 'A' object is not callable
print(a.__call__(3)) # 6
print(a.__call__.__doc__) # 'Doubles `x`.'

Above is my real question, but here's my motivation in case you're interested / think there's a better way to achieve what I'm going for. I want __call__ to be a wrapper for another instance method, in particular to have the same docstring. I could achieve this as follows:

from functools import wraps

class B:
    def double(self, x):
        """Doubles `x`."""
        return 2 * x

    @wraps(B.double)
    def __call__(self, x):
        return self.double(x)

b = B()
print(b(3)) # 6
print(b.__call__.__doc__) # 'Doubles `x`.'

But then the problem is, if I have another class that inherits from B and changes the docstring of double, the B.double.__doc__ will be used.

class B2(B):
    def double(self, x):
        """Returns twice `x`."""
        return 2 * x

b2 = B2()
print(b2(3)) # 6
print(b2.__call__.__doc__) # 'Doubles `x`.'

This is why I hoped setting __call__ to double in __init__ would help.

Nathan
  • 9,651
  • 4
  • 45
  • 65
  • 1
    I don't think there's an easy way to make the docstring update when the method is overridden. I'd honestly just set the docstring to something like "This method is an alias for the `double` method". – Aran-Fey Feb 28 '19 at 17:35
  • You now have a couple of links to show how `__call__` is used. – Prune Feb 28 '19 at 17:35
  • For future readers: special (`__` prefixed) methods in Python may be called from the class instead of the instance, so to be callable an object should have a `__call__` method on its class. It seems the only way to have `__call__`'s docstring "update" for `B2` would be to mess with `__new__` to have `B2` be a subclass of some `BTemp` that is a clone of `B` except with an updated docstring for `__call__`. – Nathan Feb 28 '19 at 17:59
  • However, some tools that parse docstrings (e.g. when using Jupyter notebooks) _will_ pull the docstring from the instance's `__class__` method instead of the class's. Thus you can define `__call__` on the class and also overwrite it in `__init__` to have a callable object which also, depending on how you check for it, has the desired docstring. – Nathan Feb 28 '19 at 18:38
  • * instance's `__call__` method – Nathan Feb 28 '19 at 18:44

0 Answers0