9

I understand that in python user-defined objects can be made callable by defining a __call__() method in the class definition. For example,

class MyClass:
  def __init__(self):
    pass

  def __call__(self, input1):
    self.my_function(input1)

  def my_function(self, input1):
    print(f"MyClass - print {input1}")

my_obj = MyClass()
# same as calling my_obj.my_function("haha")
my_obj("haha") # prints "MyClass - print haha"

I was looking at how pytorch makes the forward() method of a nn.Module object be called implicitly when the object is called and saw some syntax I didn't understand.

In the line that supposedly defines the __call__ method the syntax used is,

__call__ : Callable[..., Any] = _call_impl

This seemed like a combination of an annotation (keyword Callable[ following : ignored by python) and a value of _call_impl which we want to be called when __call__ is invoked, and my guess is that this is a shorthand for,

def __call__(self, *args, **kwargs):
    return self._call_impl(*args, **kwargs)

but wanted to understand clearly how this method of defining functions worked.

My question is: When would we want to use such a definition of callable attributes of a class instead of the usual def myfunc(self, *args, **kwargs)

0

1 Answer 1

6

Functions are normal first-class objects in python. The name to with which you define a function object, e.g. with a def statement, is not set in stone, any more than it would be for an int or list. Just as you can do

a = [1, 2, 3]
b = a

to access the elements of a through the name b, you can do the same with functions. In your first example, you could replace

def __call__(self, input1):
    self.my_function(input1)

with the much simpler

__call__ = my_function

You would need to put this line after the definition of my_function.

The key differences between the two implementations is that def __call__(... creates a new function. __call__ = ... simply binds the name __call__ to the same object as my_function. The noticeable difference is that if you do __call__.__name__, the first version will show __call__, while the second will show my_function, since that's what gets assigned by a def statement.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.