Тип возвращаемого значения в Python: от простого к сложному

Раздел: Работа с функциями -> Функции

Основы аннотации возвращаемого типа

Как указать, что функция возвращает конкретный тип?

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

def square(n: int) -> int:
    return n * n

# Пример вызова
result = square(5)
print(result)  # 25

аргументы print python (аргументы функции print в python)

25

Python 3 аргументы (аргументы в python 3)

Типичные ошибки: Если функция не возвращает значение, но указан тип, линтер может предупредить. Например, функция без return фактически возвращает None, что несовместимо с указанным типом.

# Неправильно: функция ничего не возвращает, но обещает int
def bad_square(n: int) -> int:
    print(n * n)

# mypy выдаст ошибку: Missing return statement

аргумент параметр python (аргументы и параметры в python)

Различные варианты указания типов возврата

Как указать, что функция может вернуть None или другой тип?

Для этого применяется Optional[Тип] или Union[Тип, None]. Optional - это краткая форма Union[Тип, None].

from typing import Optional, Union

def find_user(user_id: int) -> Optional[dict]:
    if user_id == 1:
        return {'name': 'Alice'}
    return None  # пользователь не найден

# Альтернатива с Union
def find_user_v2(user_id: int) -> Union[dict, None]:
    # ...
    return None

аргумент класса python (аргументы класса python)

Проблема: Использование Optional без импорта вызывает NameError. Всегда импортируйте нужные типы из typing.

Как задать несколько возможных типов возврата?

Используйте Union[Type1, Type2, ...]. Это может быть полезно при функциях, которые возвращают разные типы в зависимости от условий.

from typing import Union

def process(value: Union[int, str]) -> Union[int, str]:
    if isinstance(value, int):
        return value * 2
    else:
        return value.upper()

print(process(10))   # 20
print(process('hi')) # HI

Python аргументы строки (аргументы строки в python (командная строка))

20
HI

аргумент метода python (аргументы метода python)

Ошибка: Если функция возвращает непредусмотренный тип, статический анализатор выдаст предупреждение. Например, возвращение float при объявлении Union[int, str].

Как указать, что функция возвращает другую функцию?

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

from typing import Callable

def adder(x: int) -> Callable[[int], int]:
    def inner(y: int) -> int:
        return x + y
    return inner

add_five = adder(5)
print(add_five(3))  # 8

Python args (аргументы в python)

8

именованные аргументы функции python (именованные аргументы функции python)

Сложность: Вложенные аннотации могут стать громоздкими. Для сложных случаев используйте TypeAlias (Python 3.10+) или пользовательские псевдонимы.

Как обозначить динамический возврат (любой тип)?

Используйте Any из typing. Это отключает проверку типов для данного значения.

from typing import Any

def get_data() -> Any:
    import random
    return random.choice([42, 'hello', 3.14])

print(get_data())

именованные аргументы python (именованные аргументы python)

Когда не стоит применять Any: Чрезмерное использование Any сводит на нет преимущества типизации. Целесообразно только на границах системы (ввод-вывод, внешние библиотеки).

Как задать возврат пользовательского класса?

Указывается имя класса, определённое ранее. Можно использовать строковую аннотацию (forward reference) для ещё не определённых классов.

class User:
    def __init__(self, name: str):
        self.name = name

def create_user(name: str) -> User:
    return User(name)

# Строковая аннотация для вложенных классов
def create_wrapper() -> 'Wrapper':
    class Wrapper:
        pass
    return Wrapper()

количество аргументов функции python (количество аргументов функции python)

Продвинутые примеры аннотаций возвращаемого типа

Использование TypeVar для обобщённых функций

TypeVar позволяет задать параметризованный тип, который сохраняет связь между аргументами и результатом.

from typing import TypeVar, List

T = TypeVar('T')

def first(items: List[T]) -> T:
    return items[0]

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

параметры и аргументы функции python (параметры и аргументы функции python)

1
a

Python передать аргументы (передача аргументов в python)

Возврат итератора или генератора

Для функций-генераторов можно использовать Generator[YieldType, SendType, ReturnType] из typing.

from typing import Generator

def count_up_to(limit: int) -> Generator[int, None, None]:
    i = 1
    while i <= limit:
        yield i
        i += 1

for num in count_up_to(3):
    print(num)  # 1 2 3

переменное количество аргументов python (переменное количество аргументов python)

1
2
3

Python принять аргументы (приём аргументов в python)

Асинхронные функции и корутины

Возвращаемый тип асинхронной функции - Coroutine[Any, Any, ReturnType] (или просто Awaitable[ReturnType]).

import asyncio
from typing import Awaitable

async def fetch_data() -> Awaitable[str]:
    await asyncio.sleep(1)
    return 'data'

# Альтернативная запись:
from typing import Coroutine
async def fetch_data_v2() -> Coroutine[None, None, str]:
    await asyncio.sleep(0)
    return 'data2'

Python список аргументов (список аргументов в python)

Использование протоколов (structural subtyping)

Протоколы позволяют описывать структуру возвращаемого объекта без явного наследования.

from typing import Protocol

class HasName(Protocol):
    name: str

def get_named_object() -> HasName:
    class Person:
        def __init__(self):
            self.name = 'Alice'
    return Person()

obj = get_named_object()
print(obj.name)  # Alice
- Python функция в качестве аргумента (функция как аргумент в python)
- Python return value (возвращаемое значение функции в python)
- абсолютное значение python (абсолютное значение в python)

Расширенные примеры аннотаций возвращаемого типа

Generic с пользовательскими классами

Создание обобщённого контейнера, который возвращает элементы определённого типа.

Пример
from typing import Generic, TypeVar, List

T = TypeVar('T')

class Stack(Generic[T]):
    def __init__(self) -> None:
        self._items: List[T] = []

    def pop(self) -> T:
        return self._items.pop()

    def push(self, item: T) -> None:
        self._items.append(item)

stack = Stack[int]()
stack.push(10)
print(stack.pop())  # 10
# stack.push('str')  # mypy предупредит об ошибке
10

Тип возврата для перегруженных функций (overload)

Декоратор @overload позволяет описать несколько вариантов возвращаемого типа в зависимости от типов аргументов.

Пример
from typing import overload

@overload
def double(value: int) -> int: ...
@overload
def double(value: str) -> str: ...
def double(value):
    return value * 2

print(double(5))     # 10
print(double('ab'))  # 'abab'
10
abab

Ковариантность и контравариантность с TypeVar

Управление тем, как типы подтипов соотносятся с базовым.

Пример
from typing import TypeVar, Generic

T_co = TypeVar('T_co', covariant=True)  # ковариантный
T_contra = TypeVar('T_contra', contravariant=True)  # контравариантный

class Producer(Generic[T_co]):
    def get(self) -> T_co:
        ...

class Consumer(Generic[T_contra]):
    def put(self, value: T_contra) -> None:
        ...

Использование таких аннотаций полезно для создания типобезопасных API.

Возврат точного размера массива (Literal для кортежей)

Задание фиксированной длины и типа элементов кортежа.

Пример
from typing import Tuple, Literal

def coordinate() -> Tuple[float, float]:
    return (1.0, 2.0)

def fixed_value() -> Tuple[Literal[0], Literal[1]]:
    return (0, 1)

x, y = coordinate()

Самоссылочные аннотации (рекурсивные типы)

Для структур данных, таких как деревья.

Пример
from typing import List, Optional

class TreeNode:
    def __init__(self, value: int, children: Optional[List['TreeNode']] = None):
        self.value = value
        self.children = children or []

def build_tree() -> TreeNode:
    leaf = TreeNode(1)
    root = TreeNode(0, [leaf])
    return root

Строковая аннотация 'TreeNode' необходима, так как класс на момент определения ещё не полностью создан.

Использование NewType для уникальных типов

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

Пример
from typing import NewType

UserId = NewType('UserId', int)
PostId = NewType('PostId', int)

def get_user_name(user_id: UserId) -> str:
    return f'User {user_id}'

uid: UserId = UserId(123)
print(get_user_name(uid))
# print(get_user_name(123))  # mypy предупредит: ожидается UserId

Тип возвращаемого значения в Python - comments

En
Python тип возвращаемого значения (python)