Тип возвращаемого значения функции: инструкция по аннотациям
Основные способы указания типа возврата
Наиболее универсальный и эффективный подход - использовать синтаксис аннотации со стрелкой -> и именем типа. Этот способ работает во всех современных версиях Python (3.5+), поддерживается статическими анализаторами (mypy, pyright) и делает код более предсказуемым и документированным.
def get_age() -> int:
return 30Python type self (тип self в python)
Если функция ничего не возвращает, указывается None:
def log_message(msg: str) -> None:
print(msg)Python typing function (аннотация типа функции в python)
Типичная ошибка: забыть возвращаемое значение в теле функции. Если аннотировать -> int, а return отсутствует, функция вернёт None, и анализатор выдаст предупреждение. Решение: явно ставить -> None для процедур и проверять наличие return в ветках кода.
Как обозначить, что функция не возвращает значение?
Указывается тип None после стрелки. Это явно говорит о том, что функция выполняется ради побочных эффектов.
def write_file(path: str, data: str) -> None:
with open(path, 'w') as f:
f.write(data)
Python 3.12 type (новые возможности типов в python 3.12)
Проблема: если в теле функции присутствует return some_value, mypy сообщит об ошибке несоответствия типов. Нужно следить, чтобы все return возвращали None или отсутствовали.
Как аннотировать функцию, возвращающую один из нескольких типов (например, число или строку)?
Используется Union из модуля typing или оператор | в Python 3.10+.
from typing import Union
def parse_number(text: str) -> Union[int, float]:
try:
return int(text)
except ValueError:
return float(text)Python typing type checking (проверка типов с помощью typing в python)
В Python 3.10+ можно писать короче:
def parse_number(text: str) -> int | float:
...Python type dict (тип dict в python)
Частая ошибка: забыть импортировать Union (в старых версиях) или использовать | в Python 3.9 и ниже, что вызовет синтаксическую ошибку. Также важно помнить, что Union не должен содержать повторяющихся типов (это избыточно).
Как показать, что функция может вернуть None или значение определённого типа?
Специальная конструкция Optional[тип] (эквивалент Union[тип, None]). Рекомендуется использовать тип | None в Python 3.10+.
from typing import Optional
def find_user(user_id: int) -> Optional[dict]:
if user_id in database:
return database[user_id]
return NonePython return type (тип возврата функции в python)
Ошибка: путаница между Optional и Union. Optional[str] означает Union[str, None], а не „опциональная строка“. Если в возврате допустимы несколько типов плюс None, лучше использовать Union[тип1, тип2, None].
Как аннотировать функцию, возвращающую произвольный тип (динамический возврат)?
Используется тип Any из typing. Однако это снижает эффективность статической проверки.
from typing import Any
def get_data(source: str) -> Any:
if source == 'config':
return {'key': 'value'}
elif source == 'list':
return [1, 2, 3]
return None
Злоупотребление Any приводит к потере всех преимуществ аннотаций. Лучше использовать Union или обобщённые типы (TypeVar). Если тип действительно не определён, Any допустимо, но стоит задокументировать причину.
Как указать, что функция возвращает другую функцию?
Используется конструкция Callable[[типы_аргументов], возвращаемый_тип] из модуля typing.
from typing import Callable
def multiplier(factor: float) -> Callable[[float], float]:
def multiply(x: float) -> float:
return x * factor
return multiply
Сложность в записи сигнатуры вложенной функции. Если аргументов много или они неизвестны, можно использовать Callable[..., тип]. Однако это уменьшает точность проверки. Также нужно помнить, что Callable не поддерживает перегрузку – для этого применяются протоколы.
Как описать тип возврата генератора?
Генераторы возвращают объекты, реализующие протокол итерации. Чаще всего используют Iterator[тип] или Generator[тип_yield, тип_send, тип_return].
from typing import Iterator
def count_to(n: int) -> Iterator[int]:
for i in range(1, n + 1):
yield i
Если генератор использует send и return, нужен полный Generator:
from typing import Generator
def controlled_counter() -> Generator[int, str, bool]:
total = 0
cmd = yield total
if cmd == 'stop':
return True
return False
Ошибка: путать Iterable и Iterator. Iterable – любой объект, который можно перебирать (список, строка), а Iterator – ленивый генератор. Для генераторов следует использовать Iterator, чтобы подчеркнуть, что результат можно перебрать только один раз.
Как аннотировать асинхронную функцию?
Асинхронная функция (async def) возвращает корутину. Тип возврата – Coroutine[Any, Any, тип] из модуля typing или его упрощение – просто указать возвращаемый тип без обёртки, но это менее точно.
from typing import Coroutine
import asyncio
async def fetch_data(url: str) -> Coroutine[Any, Any, dict]:
# имитация запроса
await asyncio.sleep(1)
return {'status': 200}
Современные анализаторы (mypy) часто принимают упрощённый вариант:
async def fetch_data(url: str) -> dict:
...
Проблема: если не указать тип корутины явно, статическая проверка может быть менее строгой. Для точной проверки рекомендуется использовать Coroutine или вспомогательные типы из collections.abc.
Как сделать функцию, возвращающую тот же тип, что и аргумент (обобщённая функция)?
Используется TypeVar для связывания типов входа и выхода.
from typing import TypeVar
T = TypeVar('T')
def identity(value: T) -> T:
return value
Можно ограничить TypeVar с помощью bound или указать несколько TypeVar для разных позиций.
Распространённая ошибка: не определять TypeVar в области видимости (должен быть на уровне модуля) и не связывать типы. Если TypeVar не используется нигде, кроме одного места, лучше использовать Any или конкретный тип.
Как аннотировать функцию, возвращающую разные типы в зависимости от значения аргумента?
Применяется декоратор @overload из модуля typing. Он позволяет описать несколько сигнатур для одной реализации.
from typing import overload
@overload
def parse(data: str) -> int: ...
@overload
def parse(data: bytes) -> str: ...
def parse(data: str | bytes) -> int | str:
if isinstance(data, str):
return int(data)
return data.decode()
Проблема: перегрузка – это только подсказка для анализатора; реальная реализация должна обрабатывать все случаи самостоятельно. Если пропустить ветку, mypy не предупредит. Также нужно следить за порядком перегрузок (более конкретные должны идти первыми).
Дополнительные примеры аннотаций возврата
Пример 1. Использование Union и проверка типов в рантайме.
from typing import Union
def to_number(value: str) -> Union[int, float, None]:
try:
if '.' in value:
return float(value)
return int(value)
except ValueError:
return None
# вызов
result = to_number('42')
print(result) # 42
print(type(result)) #
42
Пример 2. Фабрика функций с Callable и замыканием.
from typing import Callable
def create_power_function(exponent: int) -> Callable[[int], int]:
def power(base: int) -> int:
return base ** exponent
return power
square = create_power_function(2)
cube = create_power_function(3)
print(square(5)) # 25
print(cube(5)) # 125
25 125
Пример 3. Генератор с полной аннотацией Generator.
from typing import Generator
def sum_coroutine() -> Generator[int, int, int]:
total = 0
while True:
value = yield total
if value is None:
break
total += value
return total
gen = sum_coroutine()
next(gen) # 0
gen.send(10) # 10
gen.send(20) # 30
try:
gen.send(None)
except StopIteration as e:
print('Total:', e.value) # Total: 30
0 10 30 Total: 30
Пример 4. Обобщённая функция с несколькими TypeVar.
from typing import TypeVar, Sequence
U = TypeVar('U')
V = TypeVar('V')
def zip_lists(a: Sequence[U], b: Sequence[V]) -> list[tuple[U, V]]:
return list(zip(a, b))
result = zip_lists([1, 2], ['a', 'b'])
print(result) # [(1, 'a'), (2, 'b')]
[(1, 'a'), (2, 'b')]
Пример 5. Использование Literal для точных возвращаемых значений.
from typing import Literal
def get_status(code: int) -> Literal['ok', 'error']:
if code == 200:
return 'ok'
return 'error'
print(get_status(200)) # ok
print(get_status(404)) # error
ok error
Пример 6. Возврат словаря с фиксированной структурой (TypedDict).
from typing import TypedDict
class Person(TypedDict):
name: str
age: int
city: str
def create_person(name: str, age: int, city: str) -> Person:
return {'name': name, 'age': age, 'city': city}
p = create_person('Alice', 30, 'Moscow')
print(p['name']) # Alice
Alice
Пример 7. Комбинация overload с разными возвращаемыми типами.
from typing import overload, Union
@overload
def process(item: int) -> int: ...
@overload
def process(item: str) -> str: ...
def process(item: Union[int, str]) -> Union[int, str]:
if isinstance(item, int):
return item * 2
return item.upper()
print(process(10)) # 20
print(process('hi')) # HI
20 HI