Вызываемые объекты: практическое руководство

Раздел: Основы Python -> Основные типы данных

Основные понятия и способы создания

Базовый способ: функция callable() и метод __call__

Вызываемый объект в Python любой объект, который поддерживает операцию вызова с круглыми скобками. Для проверки применяется встроенная функция callable(). Она возвращает True, если объект может быть вызван. К вызываемым относятся функции, классы, методы, а также любые объекты, у которых определен метод __call__. Пример проверки:

def my_func():
    pass

print(callable(my_func))  # True
print(callable(42))       # False

Python callable object (вызываемый объект в python)

True
False

Int object python (объект int в python)

Создать вызываемый объект проще всего с помощью обычной функции. Для создания объекта класса с возможностью вызова необходимо определить в классе метод __call__.

Типичная ошибка: вызов невызываемого объекта приводит к TypeError: 'int' object is not callable. Перед вызовом рекомендуется проверять callable().

Как сделать экземпляр класса вызываемым через метод __call__?

В классе определяется метод __call__. Экземпляр такого класса становится вызываемым и сохраняет состояние.

class Multiplier:
    def __init__(self, factor):
        self.factor = factor
    def __call__(self, value):
        return value * self.factor

double = Multiplier(2)
print(double(5))  # 10
print(double(7))  # 14

встроенные типы python (встроенные типы python)

10
14

Проблема: если __call__ ожидает аргументы, а при вызове их не передают, возникает TypeError. Следует проверять сигнатуру метода.

Как создать анонимную вызываемую функцию с помощью lambda?

Лямбда-выражение возвращает вызываемую анонимную функцию. Подходит для простых операций.

add = lambda a, b: a + b
print(callable(add))  # True
print(add(3, 4))      # 7
True
7

Лямбда не содержит инструкций и сложной логики. Для многострочных функций применяется def.

Как зафиксировать аргументы функции, создав новый вызываемый объект с помощью functools.partial?

functools.partial возвращает вызываемый объект, который подставляет заранее заданные аргументы.

from functools import partial

def power(base, exp):
    return base ** exp

square = partial(power, exp=2)
print(callable(square))  # True
print(square(5))         # 25
True
25

Ошибка: если передать позиционные аргументы в неправильном порядке, возможна путаница. Рекомендуется использовать именованные аргументы.

Как создать декоратор, который оборачивает функцию и возвращает вызываемый объект?

Декоратор принимает функцию и возвращает новую вызываемую обертку. Важно вернуть функцию, иначе объект не будет вызываемым.

def log_calls(func):
    def wrapper(*args, **kwargs):
        print(f"Вызов {func.__name__} с args={args} kwargs={kwargs}")
        return func(*args, **kwargs)
    return wrapper

@log_calls
def greet(name):
    return f"Привет, {name}!"

print(callable(greet))  # True
print(greet("Анна"))
Вызов greet с args=('Анна',) kwargs={}
Привет, Анна!

Если забыть вернуть внутреннюю функцию из декоратора (например, вернуть None), получится невызываемый объект. Проверка callable() помогает вовремя обнаружить ошибку.

Как использовать метод класса как вызываемый объект?

Метод класса при обращении через экземпляр или класс является вызываемым. Такой метод можно передавать в качестве аргумента.

class Calculator:
    def add(self, a, b):
        return a + b

calc = Calculator()
method = calc.add
print(callable(method))  # True
print(method(3, 5))      # 8
True
8

Разница между методом экземпляра и функцией: метод автоматически передает self. При использовании в качестве аргумента высшего порядка это следует учитывать.

Какие встроенные типы являются вызываемыми?

Функции (def, lambda, built-in), классы (вызов класса создает экземпляр), методы, объекты с __call__. Некоторые типы не вызываемы: int, str, list, dict и т.д.

print(callable(list))    # True (класс)
print(callable(int))     # True (класс)
print(callable("hello")) # False
True
True
False

Путаница между классом и экземпляром: класс вызываем, экземпляр - только если определен __call__.

Пример
class CallCounter:
    def __init__(self):
        self.count = 0
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"Вызов {self.count}: args={args}, kwargs={kwargs}")

counter = CallCounter()
print(callable(counter))  # True
counter("a", b=2)
counter(1, 2, 3)
print(f"Итого вызовов: {counter.count}")
True
Вызов 1: args=('a',) kwargs={'b': 2}
Вызов 2: args=(1, 2, 3) kwargs={}
Итого вызовов: 2
Пример
class Curry:
    def __init__(self, func, *args, **kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs
    def __call__(self, *more_args, **more_kwargs):
        combined_args = self.args + more_args
        combined_kwargs = {**self.kwargs, **more_kwargs}
        return self.func(*combined_args, **combined_kwargs)

def multiply(a, b, c):
    return a * b * c

curried = Curry(multiply, 2)
print(curried(3, 4))  # 2*3*4 = 24
print(callable(curried))  # True
24
True
Пример
import time

def timed(func):
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        wrapper.total_time += elapsed
        wrapper.calls += 1
        print(f"{func.__name__} заняла {elapsed:.6f} секунд")
        return result
    wrapper.total_time = 0.0
    wrapper.calls = 0
    return wrapper

@timed
def slow_add(a, b):
    time.sleep(0.01)
    return a + b

print(callable(slow_add))  # True
print(slow_add(1, 2))
print(slow_add(3, 4))
print(f"Всего вызовов: {slow_add.calls}, общее время: {slow_add.total_time:.6f}")
True
slow_add заняла 0.010001 секунд
3
slow_add заняла 0.010020 секунд
7
Всего вызовов: 2, общее время: 0.020021
Пример
handlers = {}

def register_handler(name, handler):
    if not callable(handler):
        raise ValueError(f"Handler {name} is not callable")
    handlers[name] = handler

def my_handler():
    print("Обработчик сработал")

register_handler("test", my_handler)
register_handler("lambda", lambda: 42)
try:
    register_handler("invalid", 123)
except ValueError as e:
    print(e)
Handler invalid is not callable
Пример
import math
print(callable(math))   # False (модуль не вызываем)
print(callable(math.sqrt)) # True (функция)
False
True

Вызываемый объект в Python - comments

En
Python callable object (python)