Тип возвращаемого значения в 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')) # HIPython аргументы строки (аргументы строки в 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)) # 8Python 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Расширенные примеры аннотаций возвращаемого типа
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