Тип функции: способы проверки и аннотации

Раздел: Основы Python -> Функции и типы

Понятие типа функции в 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))  # True

Python функция тип (тип функции в 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

Тип функции в Python - comments

En
Python функция тип (python)