__call__ in Python



Python __call__ Method

In Python, everything is treated as an object, including integers, strings, classes, and even functions. It provides a special method named __call__ that allows an instance of a class (i.e. object) to behave like a function (i.e. we can call/invoke it).

When we define the __call__ method inside a class, we can call its instance (object) using parentheses, just like a regular function.

Syntax

Following is the syntax to use the __call__ method inside a class ?

class MyClass:
   def __call__(self, *args, **kwargs):
      # Implementation of the method

Parameters

Following are the parameters of the __call__ method ?

  • *args ? It accepts any number of positional arguments passed while calling the object.
  • **kwargs ? It accepts any number of keyword arguments passed while calling the object.

Making an Object Work Like a Function Using __call__

Sometimes you may want to treat an object like a regular function. By defining the __call__ method inside a class, you can use the object itself with parentheses and pass arguments to it just like a function.

Example

In this example, we define a class CallableObject with a __call__ method that adds two numbers. When we use addition() with arguments, it behaves like a function call and returns the sum ?

# A simple class that behaves like a function 
class CallableObject:
   def __call__(self, x, y):
      # return sum of x and y
      return x + y

# Create an instance of the class
addition = CallableObject()

# Call the object like a function with arguments
result = addition(3, 4)  
print(result)

Following is the output obtained ?

7

Building Function-like Objects Using __call__

If you are building an object that behaves like a mathematical function, such as a polynomial, the __call__ method allows you to calculate the result when the object is given an input.

We can use the internal data (like coefficients of the polynomial) inside the method to perform the necessary calculations, just like how a real mathematical function works.

Example

In this example, we define a class Polynomial that stores coefficients of a polynomial. The __call__ method uses these coefficients to calculate the result. When we use quadratic with an input value, it behaves like a function call and returns the result of the polynomial expression ?

# A class that represents a polynomial function
class Polynomial:
   def __init__(self, coefficients):
      # store the coefficient's list
      self.coefficients = coefficients
   
   def __call__(self, x):
      # Calculate sum of coefficient * x^power for each term
      return sum(coefficient * x**power for power, coefficient in enumerate(self.coefficients))

# Create a quadratic polynomial: x^2 - 2x + 1
quadratic = Polynomial([1, -2, 1])

# Evaluate the polynomial at x = 3
result = quadratic(3) 
print(result)

We get the output as shown below ?

4

Using __call__ to Create a Decorator

We can also use the __call__ method to create a class-based decorator, which means using a class to add extra behaviour around a function.

In this case, the object stores a function and then wraps it using the __call__ method. When the decorated function is called, the object runs additional logic like measuring execution time before and after calling the actual function, all without changing how the function is called in the code.

Example

In this example, we define a class-based decorator TimingDecorator that wraps a function and uses the __call__ method to measure how long the function takes to run. When we use slow_function with arguments, it behaves like a function call and also prints the execution time ?

# A class-based decorator to measure function execution time
class TimingDecorator:
   def __init__(self, function):
      # store the function that needs to be decorated
      self.function = function

   def __call__(self, *args, **kwargs):
      import time
	  
      # Record the start time
      start_time = time.time()
	  
      # call the original function
      result = self.function(*args, **kwargs)
	  
      # Record the end time
      end_time = time.time()
	  
      # Print how long the function took to run
      print(f"Execution time: {end_time - start_time:.4f} seconds")
      return result   # return the function result

# Use the class as a decorator for a slow function
@TimingDecorator
def slow_function(x):
   import time
   # Pause the program for x seconds
   time.sleep(x)
   return f"Finished sleeping for {x} seconds"

# Call the decorated function
result = slow_function(2)
print(result)

After executing the above code, we get the following output ?

Execution time: 2.0021 seconds
Finished sleeping for 2 seconds

Conclusion

The __call__ method allows you to treat objects like functions. It is helpful when creating decorators, function wrappers, or custom callable objects. When used properly, it makes your Python code more organized especially in object-oriented programs.

Updated on: 2023-05-08T13:29:44+05:30

661 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements