Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Аннотации (Type Hints): Типизация в Python
Аннотация — это указание на ожидаемый тип данных для переменной, параметра функции или возвращаемого значения. В Python аннотации являются чистыми подсказками (не влияют на исполнение) и служат для документации и проверки кода инструментами вроде mypy.
Основная концепция
# Без аннотаций — что здесь функция ожидает?
def add(a, b):
return a + b
# add(5, 10)? add("hello", "world")? add([1], [2])?
# Непонятно!
# С аннотациями — ясно, что ожидается
def add(a: int, b: int) -> int:
"""Складывает два целых числа"""
return a + b
# Теперь понятно: (int, int) -> int
Важно: Python игнорирует аннотации во время исполнения!
def add(a: int, b: int) -> int:
return a + b
# Это работает (Python не проверяет типы)
add("hello", "world") # "helloworld" — OK!
add([1], [2]) # [1, 2] — OK!
# Это работает потому, что + определен для строк и списков
# Аннотации — только документация для людей и инструментов
Типизация параметров и возвращаемых значений
# Простые типы
def greet(name: str) -> str:
return f"Hello, {name}!"
def count_items(items: list) -> int:
return len(items)
def is_active(flag: bool) -> bool:
return not flag
def divide(a: float, b: float) -> float:
return a / b
# Несколько параметров
def create_user(username: str, age: int, active: bool) -> dict:
return {
"username": username,
"age": age,
"active": active
}
Сложные типы (typing module)
Для описания списков, словарей и т.д. нужен модуль typing:
from typing import List, Dict, Tuple, Optional, Union, Set
# List[T] — список элементов типа T
def process_numbers(numbers: List[int]) -> int:
return sum(numbers)
process_numbers([1, 2, 3]) # OK
process_numbers(["a", "b"]) # Аннотация скажет, что это плохо (но код выполнится)
# Dict[K, V] — словарь ключей K и значений V
def get_user_info(users: Dict[int, str]) -> str:
return users[1]
users = {1: "Alice", 2: "Bob"}
get_user_info(users) # OK
# Tuple — кортеж с фиксированными типами
def get_coordinates() -> Tuple[float, float]:
return (10.5, 20.3)
x, y = get_coordinates() # OK
# Optional[T] — может быть T или None
def find_user(user_id: int) -> Optional[Dict[str, str]]:
if user_id == 1:
return {"name": "Alice"}
return None # Может вернуть None
# Union[T1, T2, ...] — может быть одного из нескольких типов
def process_id(value: Union[int, str]) -> str:
return str(value)
process_id(123) # OK
process_id("abc") # OK
# Set[T] — множество элементов типа T
def get_unique_tags(posts: List[Dict]) -> Set[str]:
tags = set()
for post in posts:
tags.update(post.get('tags', []))
return tags
Python 3.10+: Новый синтаксис
С Python 3.10+ можно использовать встроенные типы без typing:
# Python 3.10+
def process_numbers(numbers: list[int]) -> int: # list вместо List
return sum(numbers)
def get_user(users: dict[int, str]) -> str: # dict вместо Dict
return users[1]
def get_coords() -> tuple[float, float]: # tuple вместо Tuple
return (10.5, 20.3)
def find_user(user_id: int) -> dict[str, str] | None: # | вместо Optional
if user_id == 1:
return {"name": "Alice"}
return None
Аннотации переменных
# Аннотация переменной
name: str = "Alice"
age: int = 30
height: float = 1.75
is_active: bool = True
# Можно без значения (только аннотация)
count: int # Значения нет, но тип задан
# count используется позже
count = 5
# Со сложными типами
users: List[Dict[str, str]] = [
{"name": "Alice", "email": "alice@example.com"},
{"name": "Bob", "email": "bob@example.com"}
]
config: Dict[str, Union[int, str]] = {
"timeout": 30,
"host": "localhost",
"port": 8000
}
Аннотации для функций с переменным числом аргументов
from typing import *
# *args
def sum_all(*args: int) -> int:
return sum(args)
sum_all(1, 2, 3) # OK
# **kwargs
def create_config(**kwargs: str) -> Dict[str, str]:
return kwargs
config = create_config(host="localhost", debug="true")
# Оба
def log_message(message: str, *args: str, **kwargs: str) -> None:
print(f"{message}: {args} {kwargs}")
Пользовательские типы
from typing import TypeVar, Generic
# TypeVar — переменная типа (для generic функций)
T = TypeVar('T')
def get_first(items: List[T]) -> T:
"""Возвращает первый элемент списка (сохраняя тип)"""
return items[0]
first_int = get_first([1, 2, 3]) # Тип первого значения: int
first_str = get_first(["a", "b"]) # Тип первого значения: str
# Generic класс
K = TypeVar('K')
V = TypeVar('V')
class Cache(Generic[K, V]):
def __init__(self):
self._data: Dict[K, V] = {}
def get(self, key: K) -> Optional[V]:
return self._data.get(key)
def set(self, key: K, value: V) -> None:
self._data[key] = value
# Использование
cache_int_str: Cache[int, str] = Cache()
cache_int_str.set(1, "hello")
value = cache_int_str.get(1) # Тип: Optional[str]
Практические примеры
Пример 1: Функция обработки данных
from typing import List, Dict, Optional
def filter_users(
users: List[Dict[str, any]],
active_only: bool = True
) -> List[Dict[str, any]]:
"""Фильтрует пользователей по активности"""
if active_only:
return [u for u in users if u.get('active', False)]
return users
users_data = [
{"name": "Alice", "active": True},
{"name": "Bob", "active": False}
]
active = filter_users(users_data) # OK
Пример 2: Класс с типизацией
from typing import Optional, List
from datetime import datetime
class User:
def __init__(self, name: str, age: int, email: Optional[str] = None):
self.name: str = name
self.age: int = age
self.email: Optional[str] = email
self.created_at: datetime = datetime.now()
def get_info(self) -> Dict[str, any]:
return {
"name": self.name,
"age": self.age,
"email": self.email
}
@staticmethod
def create_admin(name: str) -> 'User':
return User(name, age=0, email="admin@example.com")
user = User("Alice", 30, "alice@example.com")
info = user.get_info() # Dict[str, any]
Пример 3: Callback функции
from typing import Callable, List
def apply_to_all(
items: List[int],
processor: Callable[[int], int] # Функция (int) -> int
) -> List[int]:
"""Применяет функцию к каждому элементу"""
return [processor(item) for item in items]
# Использование
def double(x: int) -> int:
return x * 2
result = apply_to_all([1, 2, 3], double) # [2, 4, 6]
Проверка типов: mypy
Аннотации полезны, но их проверяет mypy — статический анализатор:
# Установка
pip install mypy
# Проверка файла
mypy my_code.py
# Проверка проекта
mypy .
Пример ошибки, которую найдет mypy:
# code.py
def add(a: int, b: int) -> int:
return a + b
result = add("hello", "world") # ERROR!
# mypy output:
# error: Argument 1 to "add" has incompatible type "str"; expected "int"
# error: Argument 2 to "add" has incompatible type "str"; expected "int"
Лучшие практики
# 1. Аннотируй все публичные функции
# Хорошо
def calculate(x: int) -> int:
return x * 2
# 2. Используй Optional для nullable значений
# Хорошо
def find_user(user_id: int) -> Optional[User]:
...
# Плохо (неясно, может ли быть None)
def find_user(user_id: int) -> User:
...
# 3. Не переусложняй
# Хорошо
def process(data: Dict[str, int]) -> List[int]:
...
# Плохо (слишком сложно)
from typing import Callable, Awaitable, TypeVar, Union, overload
# (редко нужно)
# 4. Используй type: ignore редко
# Плохо
result = some_untyped_library.func() # type: ignore
# Лучше
from typing import Any
result: Any = some_untyped_library.func()
Резюме
Аннотация — это указание типа для переменной, параметра или возвращаемого значения. Это не влияет на исполнение кода, но помогает:
- Документировать ожидаемые типы
- Найти ошибки с помощью mypy
- Улучшить автодополнение в IDE
- Сделать код понятнее
Аннотации критичны в production Python коде и являются лучшей практикой.