The 9 Flavors of Callable Objects in Python
Byte Sized Notes on Python Callable Objects
In Python, a Callable is an entity that can be called similar to a function, which means the Parentheses () are used to call it too.
Different Types of Callable Objects
As of Python 3.9, there are 9 callable types:
User-defined Functions: Functions created with the
def
statement or thelambda
expression.Built-in Functions: Functions implemented in the Standard Python Library usually in C language.
Built-in Methods: Methods implemented in C language for the Standard Python Library.
Methods: Functions defined in the body of a class.
Classes: Invoking a class will make it execute its
__new__
method to create an instance, then__init__
to initialize it, and eventually return the instance to the caller. Python doesn't have a new operator, so calling a class is just like calling a function.Classe Instances: Classes that have a
__call__
method can have their instances invoked as if they were functions.Generator Functions: These are Functions or Methods that return a generator object when they use the
yield
keyword in their body.Native coroutine functions: Functions or Methods defined with
async def
return a coroutine object when we call them.Asynchronous generator functions: Functions or Methods defined with
async def
that haveyield
in their body, they return an asynchronous generator to use withasync for
.
Considering the range of callable types existing in Python, the safest way to figure out if an object is callable, we use the callable() method.
print([callable(obj) for obj in (abs, str, 'Ni!')])
# [True, True, False]
def Sample():
return 5
test_var = Sample
print(callable(test_var))
# True
User Defined Callables
Any Object in Python can behave like a function and be called, to achieve this we need only to implement the __call__
Instance Method.
import random
class Carpool:
def __init__(self, passengers):
self._passengers= list(passengers)
random.shuffle(self._passengers)
def pick(self):
try:
return self._passengers.pop()
except IndexError:
raise LookupError('picking from empty car')
def __call__(self):
return self.pick()
car= Carpool(range(3))
print(car.pick())
# 1
print(Carpool())
# 0
print(callable(Carpool))
# True
Why do we need Callable Objects?
There are different situations where we might need a callable object such as:
Implementing
__call__
in a class is an easy way to create a function-like object that has some internal state that must be kept across invocations, the example above illustrates this.Decorators must be callable, and it is sometimes convenient to cache some data between calls of the decorator.