Полное руководство по аннотациям параметров и возвращаемых значений

Раздел: Функции -> Функции

Аннотация типов функций в Python

Аннотации типов (type hints) позволяют указать ожидаемые типы аргументов функции и тип возвращаемого значения. Они не влияют на выполнение кода, но улучшают читаемость, помогают IDE и статическим анализаторам (mypy, pyright) находить ошибки.

Базовое аннотирование параметров и возвращаемого значения

Самый простой и распространенный способ - указать тип после двоеточия для каждого параметра, а после стрелки -> указать тип возвращаемого значения.


def greet(name: str, age: int) -> str:
    return f'Привет, {name}! Тебе {age} лет.'

Python return value (возвращаемое значение функции в python)

В этом примере параметр name ожидает строку, age - целое число, функция возвращает строку. Аннотации игнорируются интерпретатором, но mypy при запуске mypy script.py укажет на ошибки, если передать неправильный тип.

Возможные проблемы: забыть про аннотации для сложных типов; путаница с типами при наследовании; предполагается, что все аргументы обязательны, если нет значения по умолчанию.

Как аннотировать функции, которые могут возвращать None или принимать разные типы?

Тип Optional обозначает возможность None, Union объединяет несколько типов, Any указывает на любой тип.


from typing import Optional, Union, Any

def find_user(user_id: int) -> Optional[str]:
    # возвращает имя пользователя или None
    return None

def process(value: Union[int, float, str]) -> str:
    return str(value)

def debug(message: Any) -> None:
    print(message)

абсолютное значение python (абсолютное значение в python)

Цель: точнее описать возможные типы, чтобы статический анализатор предупреждал о потенциальных None или несовместимых операциях.

Типичные ошибки: использование Optional[int] неявно включает None, но если функция никогда не возвращает None, лучше указать просто int. Неправильное использование Union может привести к излишней сложности.

Как аннотировать аргументы *args и **kwargs?

Для *args указывается тип элемента кортежа, для **kwargs - тип значения в словаре. Обычно это Tuple[Type, ...] и Dict[str, Type].


from typing import Tuple, Dict

def sum_all(*args: int) -> int:
    return sum(args)

def config(**kwargs: str) -> None:
    for key, value in kwargs.items():
        print(f'{key}: {value}')

вернуть значение функции python (возврат значения из функции в python)

Цель: описать, что args содержит только целые числа, а kwargs только строковые значения.

Проблема: если *args могут быть разных типов, используйте Union внутри Tuple. Для **kwargs, если значения разных типов, используйте Dict[str, Union[...]].

Как аннотировать функцию, принимающую другую функцию?

Тип Callable[[параметры], возвращаемый_тип] позволяет описать сигнатуру передаваемой функции.


from typing import Callable

def apply_twice(func: Callable[[int], int], value: int) -> int:
    return func(func(value))

def double(x: int) -> int:
    return x * 2

result = apply_twice(double, 5)  # 20

значения функции python (значения функции в python)

Цель: явно указать, что ожидается функция с определённой сигнатурой, что упрощает документацию и проверку.

Ошибки: часто забывают указать Callable, и тогда mypy не проверяет передаваемую функцию. Также Callable может не поддерживать переменное количество аргументов (тогда используется Callable[..., ReturnType]).

Как создать обобщенную функцию, работающую с любым типом?

TypeVar используется для обозначения переменной типа, а затем функция аннотируется с его помощью. Для классов применяется Generic.


from typing import TypeVar, List

T = TypeVar('T')

def first_element(lst: List[T]) -> T:
    return lst[0]

print(first_element([1, 2, 3]))  # 1
print(first_element(['a', 'b']))  # 'a'

вывести значение функции python (вывести значение функции в python)

Цель: сохранить информацию о типе элемента внутри функции, чтобы возвращаемый тип соответствовал входному типу.

Проблемы: неправильное использование TypeVar без ограничений (можно добавить bound или constraints). Также возможно несоответствие при множественных TypeVar.

Как сделать код более читаемым с помощью псевдонимов типов?

TypeAlias (Python 3.10+) или простое присвоение типа переменной позволяет создавать псевдонимы.


from typing import TypeAlias, List, Tuple

Point: TypeAlias = Tuple[float, float]
Polygon: TypeAlias = List[Point]

def area(poly: Polygon) -> float:
    # вычисление площади
    return 0.0

вызов функции в python (вызов функции в python)

Цель: улучшение читаемости, особенно для сложных составных типов, и лёгкость изменения определения.

Ошибки: в старых версиях Python TypeAlias не поддерживается, нужно использовать просто аннотацию переменной. Также не стоит злоупотреблять, чтобы не усложнять код.

Как аннотировать функцию, ожидающую объект с определёнными методами, без наследования?

Protocol из модуля typing (Python 3.8+) определяет ожидаемые атрибуты и методы.


from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> None: ...

def render(obj: Drawable) -> None:
    obj.draw()

class Circle:
    def draw(self) -> None:
        print('Рисую круг')

render(Circle())  # OK

Python имена функций (имена функций в python)

Цель: реализация утиной типизации с возможностью статической проверки, не требуя явного наследования.

Проблемы: в больших проектах Protocol может быть избыточным; для простых случаев достаточно аннотации с конкретным классом. Также возможна путаница с тем, что Protocol проверяет только наличие методов, но не типы.

Как аннотировать функцию-декоратор, чтобы сохранить типы обёрнутой функции?

Callable и TypeVar применяются для обобщения, часто используется functools.wraps и модуль typing.


from typing import Callable, TypeVar
import functools

F = TypeVar('F', bound=Callable[..., Any])

def log_decorator(func: F) -> F:
    @functools.wraps(func)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        print(f'Вызов {func.__name__}')
        return func(*args, **kwargs)
    return wrapper  # type: ignore

Цель: сохранить сигнатуру декорируемой функции, чтобы статический анализатор не терял информацию о типах.

Типичные ошибки: игнорирование атрибута __wrapped__ или неправильное указание возвращаемого типа. mypy может требовать явного # type: ignore.

Общие проблемы и их решение:

  • Аннотации не проверяются в рантайме - для проверки рекомендуется использовать mypy, pyright или IDE.
  • Циклические зависимости в аннотациях - применяется from __future__ import annotations (Python 3.7+) для отложенного вычисления.
  • Совместимость с версиями Python: в старых версиях недоступны некоторые типы из typing.
  • Производительность: аннотации могут замедлять импорт, но обычно это не критично.
- Python передача параметров (передача параметров в функцию в python)
- Python входные параметры (входные параметры в python)
- создание функции в python (создание функции в python)

Пример 1: Проверка типов с помощью mypy

Пример

# script.py
def add(a: int, b: int) -> int:
    return a + b

result = add(1, '2')  # Ошибка типа
$ mypy script.py
script.py:4: error: Argument 2 to \"add\" has incompatible type \"str\"; expected \"int\"
Found 1 error in 1 file (checked 1 source file)

Пояснение: mypy обнаружил несоответствие типов. Аргумент '2' является строкой, хотя ожидается int.

Пример 2: Обобщенная функция с TypeVar и ограничениями

Пример

from typing import TypeVar

Num = TypeVar('Num', int, float)

def square(x: Num) -> Num:
    return x * x

print(square(5))      # 25
print(square(3.2))    # 10.24
# print(square('5'))  # mypy ошибка
25
10.24

Пояснение: TypeVar с ограничением на int и float запрещает передачу строки. mypy выдаст ошибку при попытке использовать square('5').

Пример 3: Protocol для структурной типизации

Пример

from typing import Protocol

class Flying(Protocol):
    def fly(self) -> str: ...

class Bird:
    def fly(self) -> str:
        return 'Птица летит'

class Airplane:
    def fly(self) -> str:
        return 'Самолет летит'

def travel(thing: Flying) -> None:
    print(thing.fly())

travel(Bird())
travel(Airplane())
Птица летит
Самолет летит

Пояснение: функция travel принимает любой объект с методом fly(), даже если они не связаны наследованием. Protocol определяет структурный тип.

Пример 4: TypedDict для словарей с фиксированной структурой

Пример

from typing import TypedDict

class Person(TypedDict):
    name: str
    age: int

def greet(p: Person) -> str:
    return 'Привет, {}, возраст {}'.format(p['name'], p['age'])

user: Person = {'name': 'Иван', 'age': 30}
print(greet(user))
Привет, Иван, возраст 30

Пояснение: TypedDict позволяет аннотировать словари с конкретной структурой. mypy проверит наличие ключей и их типы.

Пример 5: Декоратор с ParamSpec (Python 3.10+)

Пример

from typing import Callable, ParamSpec, TypeVar
import functools

P = ParamSpec('P')
R = TypeVar('R')

def retry(max_attempts: int = 3) -> Callable[[Callable[P, R]], Callable[P, R]]:
    def decorator(func: Callable[P, R]) -> Callable[P, R]:
        @functools.wraps(func)
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
            for _ in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception:
                    pass
            raise RuntimeError('All attempts failed')
        return wrapper
    return decorator

@retry(max_attempts=2)
def risky_operation(x: int) -> int:
    if x < 0:
        raise ValueError
    return x * 2

print(risky_operation(5))  # 10
10

Пояснение: ParamSpec и TypeVar позволяют точно передать типы аргументов и возврата декорированной функции. mypy сохранит сигнатуру risky_operation.

Аннотация типов функций в Python - comments

En
аннотация типов функции python (python)