Тип функции: способы проверки и аннотации
Понятие типа функции в Python
Наиболее прямой способ узнать тип объекта, который вы считаете функцией, – использовать встроенную функцию type() или проверку через isinstance() с константой types.FunctionType.
from types import FunctionType
def my_func():
pass
print(type(my_func)) # <class 'function'>
print(isinstance(my_func, FunctionType)) # True
lambda_func = lambda x: x*2
print(isinstance(lambda_func, FunctionType)) # TruePython функция тип (тип функции в python)
Этот подход подходит для проверки, является ли объект функцией, определённой пользователем, включая лямбды. Однако он не охватывает встроенные функции (например, len) и методы.
Проблема:
isinstance(len, FunctionType) вернёт False, потому что len имеет тип builtin_function_or_method. Для таких случаев используйте модуль inspect.
Как проверить, является ли объект вызываемым (callable)?
Иногда достаточно знать, можно ли объект вызвать, не уточняя его точный тип. Для этого применяется функция callable().
print(callable(my_func)) # True
print(callable(42)) # False
print(callable(dict)) # True (класс тоже вызываем)
Ошибка:
callable() возвращает True для любого объекта, у которого определён метод __call__. Это могут быть классы и экземпляры с этим методом. Если нужно отличить именно функцию, используйте другие методы.
Как отличить обычную функцию от метода или лямбды?
Модуль inspect предоставляет функции isfunction, ismethod, isroutine и другие.
import inspect
class A:
def method(self): pass
def func(): pass
obj = A()
print(inspect.isfunction(func)) # True
print(inspect.ismethod(obj.method)) # True (bound method)
print(inspect.isfunction(obj.method)) # False
print(inspect.isroutine(lambda x: x)) # True (любая вызываемая процедура)
Типичная ошибка:
inspect.isfunction не распознаёт встроенные функции (например, print). Для них используйте inspect.isbuiltin.
Как задать тип функции в аннотациях?
Типизация функций в Python выполняется с помощью typing.Callable. Это удобно для передачи функции как аргумента и указания ожидаемой сигнатуры.
from typing import Callable
def apply(f: Callable[[int], int], value: int) -> int:
return f(value)
print(apply(lambda x: x + 1, 5)) # 6
Callable[[arg1_type, arg2_type], return_type] описывает функцию с определёнными типами параметров и возвращаемым значением. Для произвольной сигнатуры используйте Callable[..., ReturnType].
Проблема:
Аннотации не проверяются во время выполнения, если не используется статический анализатор (mypy, Pyright). Для динамической проверки потребуются дополнительные библиотеки.
Как создать собственный тип функции с помощью декоратора?
Декораторы могут изменять поведение функции, но тип объекта остаётся function. Чтобы создать новый тип, можно определить класс с методом __call__ и наследоваться от Callable.
from typing import Callable
class DebuggedFunction(Callable):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print(f"Вызов {self.func.__name__}")
return self.func(*args, **kwargs)
def power(x):
return x ** 2
debug_power = DebuggedFunction(power)
print(isinstance(debug_power, DebuggedFunction)) # True
print(debug_power(3)) # Вызов power -> 9
Расширенные примеры работы с типами функций
Пример 1: Фильтрация функций в модуле с помощью inspect
import inspect
import math
# Получаем все функции из модуля math
functions = []
for name, obj in inspect.getmembers(math, predicate=inspect.isroutine):
functions.append(name)
print(functions[:5]) # первые 5
# Результат: ['acos', 'acosh', 'asin', 'asinh', 'atan']
['acos', 'acosh', 'asin', 'asinh', 'atan']
Пример 2: Сравнение типов разных вызываемых объектов
import types
import inspect
def f(): pass
class Cls:
def m(self): pass
print("type(f):", type(f))
print("type(Cls.m):", type(Cls.m))
print("type(Cls().m):", type(Cls().m))
print("inspect.isfunction(f):", inspect.isfunction(f))
print("inspect.isfunction(Cls.m):", inspect.isfunction(Cls.m))
print("inspect.ismethod(Cls().m):", inspect.ismethod(Cls().m))
type(f): <class 'function'> type(Cls.m): <class 'function'> type(Cls().m): <class 'method'> inspect.isfunction(f): True inspect.isfunction(Cls.m): True inspect.ismethod(Cls().m): True
Пример 3: Использование Callable для передачи функции в качестве параметра
from typing import Callable, TypeVar
T = TypeVar('T')
def twice(func: Callable[[T], T], value: T) -> T:
return func(func(value))
print(twice(lambda x: x * 3, 2)) # 2*3=6, 6*3=18 -> 18
print(twice(lambda s: s.upper(), "hello")) # "HELLO"
18 HELLO
Пример 4: Динамическое создание функции с определённым типом
from types import FunctionType
# Создаём функцию из code object
code = compile("def dynamic(a, b): return a + b", "<string>", "exec")
func = FunctionType(code.co_consts[0], globals(), "dynamic")
print(type(func)) # <class 'function'>
print(func(5, 7)) # 12
<class 'function'> 12