Python's .__call__() Method: Creating Callable Instances

Python's .__call__() Method: Creating Callable Instances

by Leodanis Pozo Ramos May 24, 2023 intermediate python

In Python, a callable is any object that you can call using a pair of parentheses and, optionally, a series of arguments. Functions, classes, and methods are all common examples of callables in Python. Besides these, you can also create custom classes that produce callable instances. To do this, you can add the .__call__() special method to your class.

Instances of a class with a .__call__() method behave like functions, providing a flexible and handy way to add functionality to your objects. Understanding how to create and use callable instances is a valuable skill for you as a Python developer.

In this tutorial, you’ll:

  • Understand the concept of callable objects in Python
  • Create callable instances by providing your classes with a .__call__() method
  • Understand the difference between .__init__() and .__call__()
  • Code several examples of using callable instances to solve real-world problems

To get the most out of this tutorial, you should be comfortable with the basics of object-oriented programming in Python, including how to define and use classes and methods. Some familiarity with Python decorators and the strategy design pattern will also help. You should also understand the concept of state.

Understanding Callable Objects in Python

A callable in Python is any object that you can call using a pair of parentheses and a series of arguments if required. You’ll find different examples of callables in your daily interaction with Python. Some of them include:

All these different callables have something in common. They all implement the .__call__() special method. To confirm this fact, you can use the built-in dir() function, which takes an object as an argument and returns the object’s list of attributes and methods:

Python
>>> dir(abs)
[
    '__call__',
    '__class__',
    ...
]

>>> dir(all)
[
    '__call__',
    '__class__',
    ...
]

>>> def greet():
...     print("Hello, World!")
...

>>> dir(greet)
[
    '__annotations__',
    '__builtins__',
    '__call__',
    ...
]

In the first two examples, you call dir() with the built-in abs() and all() functions as arguments. In both cases, you can see that the .__call__() method is present in the output.

In the final example, you define a custom function that prints a message to the screen. This function also has .__call__(). Note how you can use this method to call the function:

Python
>>> greet.__call__()
Hello, World!

Note that using .__call__() as you did in this example produces the same effect as calling the function directly with greet().

Now, how does all this work internally? When you run something like callable_object(*args, **kwargs), Python internally translates the operation into callable_object.__call__(*args, **kwargs). The arguments to the regular function are the same as those used in .__call__(). In other words, whenever you call a callable object, Python automatically runs its .__call__() method behind the scenes using the arguments you’ve passed into the callable.

Now take a look at the following custom class:

Python
>>> class SampleClass:
...     def method(self):
...         print("You called method()!")
...

>>> type(SampleClass)
<class 'type'>

>>> dir(type)
[
    '__abstractmethods__',
    '__annotations__',
    '__base__',
    '__bases__',
    '__basicsize__',
    '__call__',
    ...
]

>>> sample_instance = SampleClass()
>>> dir(sample_instance.method)
[
    '__call__',
    '__class__',
    ...
]

In Python, everything is an object. Classes like SampleClass are objects of type, which you can confirm by calling type() with the class object as an argument or by accessing the .__class__ attribute.

The class constructor of SampleClass falls back to using type.__call__(). That’s why you can call SampleClass() to get a new instance. So, class constructors are callable objects that return new instances of the underlying class.

In the example above, you can observe that method objects, like sample_instance.method, also have a .__call__() special method that turns them into callable objects. The main takeaway here is that to be callable, an object needs to have a .__call__() method.

If you inspect a closure, generator function, or asynchronous function, then you’ll get similar results. You’ll always find a .__call__() method in callable objects.

Checking Whether an Object Is Callable

If you ever need to check whether a Python object is callable, then you can use the built-in callable() function like in the following examples:

Python
>>> callable(abs)
True
>>> callable(all)
True

>>> callable(greet)
True

>>> callable(SampleClass)
True

>>> callable(sample_instance)
False

The callable() function takes an object as an argument and returns True if the object is callable. Otherwise, it returns False.

In the above examples, all the tested objects are callable except for sample_instance. That’s foreseeable because SampleClass doesn’t implement a .__call__() method for its instances. Yes, you guessed it! You can make the instances of your custom classes callable by writing a .__call__() method. In the following section, you’ll learn the basics of turning the instances of your classes into callable objects.

But first, it’s important to note that sometimes callable() may produce false positives:

Python
>>> class NonCallable:
...     def __call__(self):
...         raise TypeError("not really callable")
...

>>> instance = NonCallable()
>>> callable(instance)
True

>>> instance()
Traceback (most recent call last):
    ...
TypeError: not really callable

In this example, callable() returns True. However, instances of this custom class aren’t callable, and you get an error if you try to call them. So, callable() only guarantees that the target instance comes from a class that implements a .__call__() method.

Creating Callable Instances With .__call__() in Python

If you want the instances of a given class to be callable, then you need to implement the .__call__() special method in the underlying class. This method enables you to call the instances of your class as you’d call regular Python functions.

Unlike other special methods, .__call__() doesn’t have special requirements for what arguments it must accept. It works like any other instance method in the sense that it takes self as its first argument and can take as many extra arguments as you need.

Here’s an example of how instances of a class with a .__call__() method work:

Python
# counter.py

class Counter:
    def __init__(self):
        self.count = 0

    def increment(self):
        self.count += 1

    def __call__(self):
        self.increment()

In this Counter class, you have a .count instance attribute to keep track of the current count. Then you have an .increment() method that adds 1 to the count every time you call it. Finally, you add a .__call__() method. In this example, .__call__() falls back to calling .increment(), providing a shortcut for running the increment operation.

Take a look at how the class works in practice:

Python
>>> from counter import Counter

>>> counter = Counter()

>>> counter.increment()
>>> counter.count
1

>>> counter()
>>> counter.count
2
>>> counter()
>>> counter.count
3

After creating an instance of Counter, you call .increment(). This call increments the .count attribute by 1, as you can confirm by accessing the attribute. In the rest of the examples, you take advantage of the fact that your class has a .__call__() method and call the instance directly to increment the count.

In this example, .__call__() provides a quick shortcut for running the count increment operation. This feature gives your class a convenient and user-friendly interface.

The .__call__() method in the above example doesn’t take any arguments. The method doesn’t return an explicit value either. However, there are no restrictions on how to write the .__call__() method in your custom classes. So, you can make them take arguments, return values, and even cause side effects like in your Counter class example.

For a second example, consider the following class, which allows you to create callable objects to compute different powers:

Python
# power.py

class PowerFactory:
    def __init__(self, exponent=2):
        self.exponent = exponent

    def __call__(self, base):
        return base**self.exponent

In this example, your PowerFactory class takes exponent as an argument, which you’ll use later to run different power operations. The .__call__() method takes a base argument and calculates its power using the previously provided exponent. Finally, the method returns the computed result.

Here’s your class in action:

Python
>>> from power import PowerFactory

>>> square_of = PowerFactory(2)
>>> square_of(3)
9
>>> square_of(6)
36

>>> cube_of = PowerFactory(3)
>>> cube_of(3)
27
>>> cube_of(6)
216

Here, you use PowerFactory to create two different callable instances. The first instance raises numbers to the power of 2, while the second instance raises numbers to the power of 3.

In this example, you need to pass base as an argument when calling square_of() or cube_of() because those calls fall back to calling .__call__(), which takes a base argument. Finally, note how you get the power back from every call. That’s because .__call__() returns the result of calculating the desired power.

Defining a .__call__() method in custom classes allows you to use the instances of those classes as regular Python functions. This feature can come in handy in several situations, as you’ll learn in the section Putting Python’s .__call__() Into Action.

Before jumping into common use cases of callable instances, you’ll explore the differences between the .__init__() and .__call__() methods. These two methods and their corresponding roles in Python classes can be confusing for people who are beginning to work with Python.

Understanding the Difference: .__init__() vs .__call__()

Differentiating the roles of .__init__() and .__call__() in a Python class can be a confusing task for developers who are starting to study the language or its object-oriented features. However, these two methods are pretty different, and each one has specific goals.

The .__init__() method is the instance initializer. Python calls this method automatically whenever you create an instance of a class by calling the class constructor. The arguments to .__init__() will be the same as the arguments to the class constructor, and they’ll typically provide initial values for instance attributes.

Meanwhile, the .__call__() method turns instances into callable objects. As you already learned, Python automatically calls this method whenever you call a concrete instance of a given class.

To illustrate the differences between both methods, consider the following example class:

Python
>>> class Demo:
...     def __init__(self, attr):
...         print(f"Initialize an instance of {self.__class__.__name__}")
...         self.attr = attr
...         print(f"{self.attr = }")
...
...     def __call__(self, arg):
...         print(f"Call an instance of {self.__class__.__name__} with {arg}")
...

This Demo class implements both .__init__() and .__call__(). In .__init__(), you print a message and initialize the .attr attribute. In .__call__(), you only print a message so that you learn when the method gets called with a given argument.

Here’s how this class works:

Python
>>> demo = Demo("Some initial value")
Initialize an instance of Demo
self.attr = 'Some initial value'

>>> demo("Hello!")
Call an instance of Demo with Hello!

As you can see, each method has a different role in your class. The .__init__() method gets called when you create instances of the class. Its main goal is to initialize instance attributes with sensible initial values.

You’ll find .__init__() in all Python classes. Some classes will have an explicit implementation, and others will inherit the method from a parent class. In many cases, object is the class that provides this method:

Python
>>> dir(object)
[
    ...
    '__gt__',
    '__hash__',
    '__init__',
    ...
]

Remember that object is the ultimate parent class of all Python classes. So, even if you don’t define an explicit .__init__() method in one of your custom classes, that class will still inherit the default implementation from object.

In contrast, the .__call__() method runs when you call a concrete instance of its containing class, such as demo in this example. The goal of .__call__() is to turn your instances into callable objects. In other words, its purpose is to create objects that you can call as you would call a regular function. Most Python classes don’t implement this method. Your custom classes will have it only if you need to use your instances as functions.

Great! After clarifying the differences between .__call__() and .__init__(), you’re ready to continue learning about .__call__() by discovering how you can take advantage of this method in your Python code.

Putting Python’s .__call__() Into Action

Writing classes that produce callable instances can be pretty useful in a few situations. For example, you can take advantage of callable instances when you need to:

  • Retain state between calls
  • Cache values that result from previous computations
  • Implement straightforward and convenient APIs

Even though you can use functions or classes with regular methods to solve all these problems, using callable instances may be a good option in some situations. This is especially true when you already have an existing class and face the need for function-like behavior.

In the following sections, you’ll write practical examples that illustrate each of these use cases of callable instances in Python.

Writing Stateful Callables

Sometimes, you may want to write callable objects that retain state between calls, which are commonly known as stateful callables. For example, say that you want to write a callable that takes consecutive numeric values from a data stream and computes their cumulative average. Between calls, the callable must keep track of previously passed values.

To solve this problem, you can use a closure that looks something like this:

Python
>>> def cumulative_average():
...     data = []
...     def average(new_value):
...         data.append(new_value)
...         return sum(data) / len(data)
...     return average
...

>>> stream_average = cumulative_average()

>>> stream_average(12)
12.0
>>> stream_average(13)
12.5
>>> stream_average(11)
12.0
>>> stream_average(10)
11.5

In cumulative_average(), you use a local variable called data to hold the data between calls. Then you define an inner function called average(). This function takes a new value in each call and appends it to data. Then the function computes and returns the average of the currently stored data.

Finally, cumulative_average() returns the inner function. In practice, it returns a closure, which is a special object that packs together the average() function and its non-local scope. In this example, that closure includes the data variable.

Once you’ve finished writing cumulative_average(), then you can create custom closures like stream_average. This object is callable, so you can use it as a function to compute the cumulative average of a data stream, as you did in the final examples above.

Even though closures allow you to retain state between calls, these tools may be hard to understand and process. In this sense, writing a class with a .__call__() method can facilitate the task and make your code more readable and explicit.

Here’s how you can solve the above problem using a class with a .__call__() method:

Python
# cumulative_average.py

class CumulativeAverager:
    def __init__(self):
        self.data = []

    def __call__(self, new_value):
        self.data.append(new_value)
        return sum(self.data) / len(self.data)

In this example, your class has an instance attribute called .data to hold the data. The .__call__() method takes a new value in each call, appends the value to .data, and finally computes and returns the average.

In this case, your code is quite readable. The .data attribute retains the state between calls, while the .__call__() method computes the cumulative average. Here’s how this class works in practice:

Python
>>> from cumulative_average import CumulativeAverager

>>> stream_average = CumulativeAverager()
>>> stream_average(12)
12.0
>>> stream_average(13)
12.5
>>> stream_average(11)
12.0
>>> stream_average(10)
11.5
>>> stream_average.data
[12, 13, 11, 10]

The instances of CumulativeAverager are callables that retain the previously entered values and compute the cumulative average in each call. This approach makes your code easier to reason about. To write this class, you don’t have to know about the intricacies of how closures work in Python.

Another interesting advantage is that now you have direct access to the current data through the .data attribute.

Caching Computed Values

Another common use case of callable instances is when you need a stateful callable that caches computed data between calls. This will be handy when you need to optimize some algorithms.

For example, say that you want to compute the factorial of a given number. Because you plan to run this computation multiple times, you need to make it efficient. A way to do this is to cache the already-computed values so that you don’t have to recompute them all the time.

Here’s a class that achieves this result using .__call__() and caching:

Python
# factorial.py

class Factorial:
    def __init__(self):
        self.cache = {0: 1, 1: 1}

    def __call__(self, number):
        if number not in self.cache:
            self.cache[number] = number * self(number - 1)
        return self.cache[number]

In this class, you use a dictionary to cache already-computed factorial values. The dictionary keys hold already-passed numbers, and the dictionary values hold already-calculated factorials.

The .__call__() method checks if the current input number is already in the .cache dictionary. If that’s the case, then the method returns the corresponding value without running the computation again. This behavior optimizes your algorithm, making it faster.

If the current input number isn’t in the .cache dictionary, then the method computes the factorial recursively, caches the result, and returns the final value to the caller.

Here’s how this class works:

Python
>>> from factorial import Factorial

>>> factorial_of = Factorial()

>>> factorial_of(4)
24
>>> factorial_of(5)
120
>>> factorial_of(6)
720

>>> factorial_of.cache
{0: 1, 1: 1, 2: 2, 3: 6, 4: 24, 5: 120, 6: 720}

Each call to the instance of Factorial checks the cache for already-computed values. The instance only computes factorials for those numbers that haven’t been passed yet. Note how all the input values and their corresponding factorials end up stored in the .cache dictionary.

Creating Clear and Convenient APIs

Writing classes that produce callable instances also allows you to design convenient and user-friendly application programming interfaces (APIs) for your libraries, packages, and modules.

For example, say you’re writing a new and cool library for creating GUI applications. Your library will have a MainWindow class that provides all the functionalities to create the main window of your GUI apps.

The class will have several methods, including a .show() method to draw the window on the screen. In this case, you can provide a .__call__() method like the following:

Python
class MainWindow:
    def show(self):
        print("Showing the app's main window...")

    def __call__(self):
        self.show()

    # ...

In this example, the .__call__() method falls back to calling the .show() method. This implementation enables you to show your main window by calling either .show() or the window instance itself:

Python
window = MainWindow()
window()  # Or window.show()

In this example, .__call__() provides a handy shortcut to display the app’s window on your screen. This can improve your user’s experience. So, this trick is a great way to create user-friendly and intuitive interfaces for your Python projects.

Another use case where .__call__() can help you improve your APIs is when you have a class whose primary purpose is to provide a single action or behavior. For example, say you want a Logger class that takes care of logging messages to a file:

Python
# logger.py

class Logger:
    def __init__(self, filename):
        self.filename = filename

    def __call__(self, message):
        with open(self.filename, mode="a", encoding="utf-8") as log_file:
            log_file.write(message + "\n")

In this example, the main purpose of Logger is to write messages to a log file that you should provide. By implementing the .__call__() method, you provide a shortcut for accessing this functionality by calling the object like a function.

Exploring Advanced Use Cases of .__call__()

So far, you’ve learned a lot about creating callable instances using the .__call__() method in your classes. This method also has some advanced use cases in Python. One of these use cases is when you want to create class-based decorators. In this situation, the .__call__() method is the only way to go because it enables callable instances.

Another interesting use case of .__call__() is when you need to implement the strategy design pattern in Python. In this case, you can take advantage of .__call__() to create classes that provide implementations for your different strategies.

In the following sections, you’ll learn how to use .__call__() to create class-based decorators and also to implement the strategy pattern in Python.

Writing Class-Based Decorators

Python’s decorators are callables that take another callable as an argument and extend its behavior without explicitly modifying its code. Decorators provide an excellent tool for adding new functionality to existing callables.

It’s pretty common to find and write function-based decorators. However, you can also write class-based decorators by taking advantage of the .__call__() special method.

To illustrate how you can do this, say that you want to create a decorator that measures the execution time of your custom functions. The code below shows how you can write this decorator as a class:

Python
# timing.py

import time

class ExecutionTimer:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        start = time.perf_counter()
        result = self.func(*args, **kwargs)
        end = time.perf_counter()
        print(f"{self.func.__name__}() took {(end - start) * 1000:.4f} ms")
        return result

The ExecutionTimer class takes a function object as an argument at instantiation time. The .__call__() method operates on that function object. In this example, .__call__() uses the *args and **kwargs generic arguments to handle any arguments that the input function requires.

Next, you use time.perf_counter() to get the time before and after the input function executes. Then you print a message with the function’s name and the execution time in milliseconds. The final step is to return the result of calling the input function.

With this class in place, you can start measuring the execution time of your Python functions right away:

Python
>>> from timing import ExecutionTimer

>>> @ExecutionTimer
... def square_numbers(numbers):
...     return [number ** 2 for number in numbers]
...

>>> square_numbers(list(range(100)))
square_numbers() took 0.0069 ms
[
    0,
    1,
    4,
    9,
    16,
    25,
    ...
]

In this code snippet, you have a function that takes a list of numbers and returns a list of square values. You want to measure the execution time of this function, so you use your @ExecutionTimer decorator to that end.

Once the function is decorated, whenever you run it, you get a message with the function name and the execution time in milliseconds. Then you get the function’s return value.

Now say that you want to add a repetitions argument to your decorator. This argument will allow you to run the input function several times and compute the average execution time:

Python
# timing.py

import time

class ExecutionTimer:
    def __init__(self, repetitions=1):
        self.repetitions = repetitions

    def __call__(self, func):
        def timer(*args, **kwargs):
            result = None
            total_time = 0
            print(f"Running {func.__name__}() {self.repetitions} times")
            for _ in range(self.repetitions):
                start = time.perf_counter()
                result = func(*args, **kwargs)
                end = time.perf_counter()
                total_time += end - start
            average_time = total_time / self.repetitions
            print(
                f"{func.__name__}() takes "
                f"{average_time * 1000:.4f} ms on average"
            )
            return result

        return timer

This updated version of ExecutionTimer is quite different from your original implementation. The class initializer takes repetitions an argument that you need to provide as part of the decorator call.

In .__call__(), you take the input function as an argument. Then you create an inner function to process the input function’s execution. Inside this inner function, you use a for loop to run the input function multiple times and compute the total execution time.

Next, you calculate the average execution time and print an informative message as usual. Finally, you return the input function’s result. Note that .__call__() returns the function object represented by timer.

With these changes in place, go ahead and try ExecutionTimer again. Note that to access this new version of ExecutionTimer, you need to reload the timing.py file or restart your current interactive session first:

Python
>>> from timing import ExecutionTimer

>>> @ExecutionTimer(repetitions=100)
... def square_numbers(numbers):
...     return [number ** 2 for number in numbers]
...

>>> square_numbers(list(range(100)))
Running square_numbers() 100 times
square_numbers() takes 0.0073 ms on average
[
    0,
    1,
    4,
    9,
    16,
    25,
    ...
]

Your decorator now allows you to run the target function a specific number of times and calculate the average execution time. That’s great!

Implementing the Strategy Design Pattern

The strategy design pattern allows you to define a family of similar algorithms and make them interchangeable at runtime. In other words, the pattern implements different solutions to a given type of problem, with each solution bundled in a specific object. Then, you can choose the appropriate solution dynamically.

As an example of how to use .__call__() to implement the strategy pattern, say that you need to serialize some data into JSON or YAML, depending on certain conditions. In this case, you can use the strategy pattern. You’ll have a class to serialize data into JSON and another class to serialize data into YAML.

In the example below, you’ll code a possible solution to your problem. Note that for the example to work, you first need to install pyyaml using pip because the Python standard library doesn’t offer appropriate tools for processing YAML data. It’s a missing battery.

Here’s your code:

Python
# serializing.py

import json

import yaml

class JsonSerializer:
    def __call__(self, data):
        return json.dumps(data, indent=4)

class YamlSerializer:
    def __call__(self, data):
        return yaml.dump(data)

class DataSerializer:
    def __init__(self, serializing_strategy):
        self.serializing_strategy = serializing_strategy

    def serialize(self, data):
        return self.serializing_strategy(data)

In this example, you have the JsonSerializer and YamlSerializer classes, which represent your serializing strategies. Their .__call__() methods use appropriate tools to serialize the input data into JSON and YAML, respectively.

Then you have the DataSerializer class, which provides the higher-level class. You’ll use this class to serialize your data. First, you need to provide an appropriate callable instance of a concrete serializer class:

Python
>>> from serializing import DataSerializer, JsonSerializer, YamlSerializer

>>> data = {
...     "name": "Jane Doe",
...     "age": 30,
...     "city": "Salt Lake City",
...     "job": "Python Developer",
... }

>>> serializer = DataSerializer(JsonSerializer())
>>> print(f"JSON:\n{serializer.serialize(data)}")
JSON:
{
    "name": "Jane Doe",
    "age": 30,
    "city": "Salt Lake City",
    "job": "Python Developer"
}

>>> # Switch strategy
>>> serializer.serializing_strategy = YamlSerializer()
>>> print(f"YAML:\n{serializer.serialize(data)}")
YAML:
age: 30
city: Salt Lake City
job: Python Developer
name: Jane Doe

In this code snippet, you have a dictionary containing some sample data. To process this data, you create an instance of DataSerializer using JsonSerializer as an argument. After this step, your instance can convert your dictionary into JSON.

In the final example, you change the serializing strategy and use your data serializer to convert the data into YAML code. Do you have ideas for other useful data serializers?

Conclusion

You’ve learned a lot about callable instances in Python, especially how to define them using the .__call__() special method in your custom classes. Now you know how to create classes that produce objects that you can call just like regular functions. This allows you to add flexibility and functionality to your object-oriented programs.

In this tutorial, you’ve learned how to:

  • Comprehend callables in Python
  • Write callable instances by using the .__call__() method
  • Grasp the difference between .__init__() and .__call__()
  • Implement various examples of using callable instances to tackle practical issues

With this knowledge, you can design and implement callable instances in your Python code. This will allow you to solve various common problems, such as retaining state between calls, caching data, writing class-based decorators, and more.

🐍 Python Tricks 💌

Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

About Leodanis Pozo Ramos

Leodanis Pozo Ramos Leodanis Pozo Ramos

Leodanis is an industrial engineer who loves Python and software development. He's a self-taught Python developer with 6+ years of experience. He's an avid technical writer with a growing number of articles published on Real Python and other sites.

» More about Leodanis

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Master Real-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

Master Real-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. Get tips for asking good questions and get answers to common questions in our support portal.


Looking for a real-time conversation? Visit the Real Python Community Chat or join the next “Office Hours” Live Q&A Session. Happy Pythoning!

Keep Learning

Related Tutorial Categories: intermediate python