← Назад к вопросам

Какие знаешь callable-объект?

1.8 Middle🔥 191 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Callable объекты в Python

Callable-объект (вызываемый объект) — это любой объект, который можно вызвать с круглыми скобками (). Это один из ключевых концептов Python, позволяющих писать гибкий и функциональный код.

1. Функции — базовые callable-объекты

# Обычная функция
def greet(name):
    return f"Hello, {name}!"

# Функция — это callable-объект
print(callable(greet))  # True

# Можно вызвать
result = greet("Alice")  # "Hello, Alice!"

# Можно передать как аргумент
def execute_function(func, arg):
    return func(arg)

execute_function(greet, "Bob")  # "Hello, Bob!"

# Можно сохранить в переменную
my_func = greet
my_func("Charlie")  # "Hello, Charlie!"

# Можно вернуть из функции
def create_greeter(prefix):
    def greet(name):
        return f"{prefix} {name}!"
    return greet  # Возвращаем функцию!

greeter = create_greeter("Hi")
greeter("David")  # "Hi David!"

2. Lambda функции — анонимные функции

# Lambda — короткая функция без def
square = lambda x: x ** 2
print(square(5))  # 25

# Используется в map, filter, sorted
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))  # [1, 4, 9, 16, 25]

# Фильтрация
evens = list(filter(lambda x: x % 2 == 0, numbers))  # [2, 4]

# Сортировка по сложному ключу
students = [
    {'name': 'Alice', 'grade': 85},
    {'name': 'Bob', 'grade': 92},
    {'name': 'Charlie', 'grade': 78}
]

sorted_by_grade = sorted(students, key=lambda s: s['grade'], reverse=True)
# [
#   {'name': 'Bob', 'grade': 92},
#   {'name': 'Alice', 'grade': 85},
#   {'name': 'Charlie', 'grade': 78}
# ]

3. Встроенные функции (built-ins)

# Built-in функции тоже callable
print(callable(len))  # True
print(callable(print))  # True
print(callable(int))  # True
print(callable(str))  # True

# Примеры использования
text = "hello"
print(len(text))  # 5
print(int("42"))  # 42
print(str(123))  # "123"

# Конструкторы классов тоже callable
class Person:
    def __init__(self, name):
        self.name = name

person = Person("Alice")  # Вызов конструктора
print(callable(Person))  # True

4. Методы — callable объекты класса

class Calculator:
    def __init__(self, initial_value=0):
        self.value = initial_value
    
    def add(self, x):
        """Метод экземпляра"""
        self.value += x
        return self.value
    
    @classmethod
    def create_with_value(cls, value):
        """Метод класса"""
        return cls(value)
    
    @staticmethod
    def validate_number(x):
        """Статический метод"""
        return isinstance(x, (int, float))

calc = Calculator(10)
print(callable(calc.add))  # True
print(calc.add(5))  # 15

# Класс-метод
calc2 = Calculator.create_with_value(100)
print(calc2.value)  # 100

# Статический метод
print(Calculator.validate_number(42))  # True

5. Классы — callable-объекты

# Класс — это callable-объект
print(callable(str))  # True
print(callable(list))  # True
print(callable(dict))  # True

# Вызов класса создаёт экземпляр
mylist = list([1, 2, 3])
mydict = dict(a=1, b=2)

# Собственный класс
class MyClass:
    def __init__(self, value):
        self.value = value
    
    def __call__(self, x):
        """Делает экземпляр класса callable!"""
        return self.value + x

obj = MyClass(10)
print(callable(obj))  # True
print(obj(5))  # 15 — вызов метода __call__

6. Функции с call методом (callable классы)

# Объект, который ведёт себя как функция
class Multiplier:
    """Класс, который умеет умножать"""
    
    def __init__(self, factor):
        self.factor = factor
    
    def __call__(self, value):
        """Делает экземпляр callable"""
        return value * self.factor

# Используем как функцию
double = Multiplier(2)
triple = Multiplier(3)

print(double(5))  # 10
print(triple(5))  # 15

# Можно передать как аргумент
def apply_multiplier(multiplier_obj, value):
    return multiplier_obj(value)  # Вызывает __call__

print(apply_multiplier(double, 7))  # 14

# Практический пример: логирование
class Logger:
    def __init__(self, level):
        self.level = level
    
    def __call__(self, message):
        print(f"[{self.level}] {message}")

error_log = Logger("ERROR")
info_log = Logger("INFO")

error_log("Something went wrong")  # [ERROR] Something went wrong
info_log("Process started")  # [INFO] Process started

7. Декораторы — функции, работающие с callable

# Декоратор — это функция, которая принимает callable и возвращает callable
def timer(func):
    """Декоратор, измеряющий время выполнения"""
    import time
    
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end - start:.4f} seconds")
        return result
    
    return wrapper

@timer
def slow_function():
    time.sleep(1)
    return "Done"

slow_function()  # slow_function took 1.0001 seconds

# Декоратор с параметрами
def retry(max_attempts=3):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts:
                        raise
                    print(f"Attempt {attempt} failed, retrying...")
        return wrapper
    return decorator

@retry(max_attempts=3)
def unstable_function():
    import random
    if random.random() < 0.7:
        raise ValueError("Random error")
    return "Success"

8. Встроенные функции высшего порядка

# map — применить функцию к каждому элементу
values = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, values))
print(squared)  # [1, 4, 9, 16, 25]

# filter — выбрать элементы по условию
even_numbers = list(filter(lambda x: x % 2 == 0, values))
print(even_numbers)  # [2, 4]

# reduce — свернуть список в одно значение
from functools import reduce

sum_all = reduce(lambda a, b: a + b, values)
print(sum_all)  # 15

product = reduce(lambda a, b: a * b, values)
print(product)  # 120

# sorted с callable key
words = ["apple", "pie", "a", "python"]
sorted_by_length = sorted(words, key=len)
print(sorted_by_length)  # ['a', 'pie', 'apple', 'python']

sorted_by_vowels = sorted(words, key=lambda w: sum(1 for c in w if c in 'aeiou'))
print(sorted_by_vowels)  # ['a', 'pie', 'python', 'apple']

9. Проверка callable-объектов

from typing import Callable
import inspect

def my_function():
    pass

class MyClass:
    def __call__(self):
        pass

obj = MyClass()

# callable() — встроенная проверка
print(callable(my_function))  # True
print(callable(MyClass))  # True
print(callable(obj))  # True
print(callable("string"))  # False
print(callable(42))  # False

# inspect модуль для анализа
print(inspect.isfunction(my_function))  # True
print(inspect.isclass(MyClass))  # True
print(inspect.ismethod(obj.my_function))  # False (это callable!)

# Type hints
def execute(func: Callable[[int], str]) -> str:
    """Функция принимает callable, которая берёт int и возвращает str"""
    return func(42)

result = execute(lambda x: f"Number: {x}")
print(result)  # "Number: 42"

10. functools — утилиты для работы с callable

from functools import partial, wraps

# partial — создать функцию с зафиксированными аргументами
def multiply(x, y):
    return x * y

double = partial(multiply, 2)
print(double(5))  # 10
print(double(10))  # 20

# wraps — сохранить метаданные функции при оборачивании
def my_decorator(func):
    @wraps(func)  # Сохраняет __name__, __doc__, etc.
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def greet(name):
    """Greeting function"""
    return f"Hello, {name}!"

print(greet.__name__)  # "greet" (не "wrapper"!)
print(greet.__doc__)  # "Greeting function"

11. Практические примеры

# Фабрика функций
def create_validator(min_val, max_val):
    def validator(value):
        return min_val <= value <= max_val
    return validator

validate_age = create_validator(0, 120)
validate_score = create_validator(0, 100)

print(validate_age(25))  # True
print(validate_score(150))  # False

# Callback функции
class Button:
    def __init__(self, label):
        self.label = label
        self.on_click = None  # callback будет сохранён здесь
    
    def click(self):
        if self.on_click:
            self.on_click()  # Вызов callback

button = Button("Save")
button.on_click = lambda: print("Saving...")
button.click()  # "Saving..."

# Strategy pattern — выбор алгоритма
class PaymentProcessor:
    def __init__(self, strategy: Callable[[float], bool]):
        self.strategy = strategy
    
    def process(self, amount: float) -> bool:
        return self.strategy(amount)

def credit_card_payment(amount):
    print(f"Processing credit card payment of {amount}")
    return True

def paypal_payment(amount):
    print(f"Processing PayPal payment of {amount}")
    return True

processor1 = PaymentProcessor(credit_card_payment)
processor1.process(100)  # Processing credit card payment of 100

processor2 = PaymentProcessor(paypal_payment)
processor2.process(100)  # Processing PayPal payment of 100

Таблица типов callable-объектов

ТипПримерcallable()Примечание
Функцияdef f(): passTrueОбычная функция
Lambdalambda x: x+1TrueАнонимная функция
Встроеннаяlen, printTrueBuilt-in функции
Классclass C: passTrueКонструктор
Методobj.methodTrueМетод экземпляра
Метод класса@classmethodTrueМетод класса
Статический метод@staticmethodTrueСтатический метод
callobj с __call__TrueCallable объект
Встроенный метод'str'.upperTrueСтроковый метод
Partialpartial(f, x)TrueЧастично применённая функция

Best Practices

  1. Используй callable для гибкости — позволяет передавать стратегии и алгоритмы
  2. Проверяй callable перед вызовомif callable(obj): obj()
  3. Используй type hintsCallable[[int, str], bool] для типизации
  4. Сохраняй метаданные с @wraps — при оборачивании функций
  5. Lambda для простых операций — для сложного кода используй def
  6. Классы с call для состояния — когда нужно сохранить данные