Все мы знаем, что Python - язык с динамической типизацией. Но с довольно давних пор в нем также появилась возможность явно указывать типы данных и это здорово матчится с принципом дзена Python: явное лучше неявного (Explicit is better than implicit).
Зачем их вообще указывать?
Да, аннотации являются необязательными, но я бы сказал, что это правила хорошего тона для программистов. Важно четко понимать, с чем ты работаешь. Это поможет избежать ошибок (зачастую нетривиальных), когда параметр обрабатывает несколько типов данных или принимает какие-то сложные объекты. Если вы работаете в большой команде, это просто облегчит вам жизнь.
Как указывать аннотации?
Когда мы прописываем тип данных для какого-то объекта или параметра - просто через двоеточие, для возвращаемых типов - указываем через стрелочку.
Несколько примеров:
Встроенные типы и модуль typing
name: str = "Dude"
age: int = 49
height: float = 185.1
Iterable
Для итерируемых объектов мы можем явно указать тип Iterable, когда нам не принципиально, что именно это будет - список, кортеж и т.д.
from typing import Iterable
def my_func(param: Iterable[int]) -> None:
for num in param:
print(num)
my_func(param=(1, 2, 3))
my_func(param={1, 2, 3})
my_func(param=[1, 2, 3])
Union, Optional, Any
Когда нам нужно использовать несколько типов, мы можем использовать тип Union из модуля typing, или просто символ | между типами (второе предпочтительнее).
from typing import Union
param_1: Union[str, int]
param_2: str | int
Для опциональных значений существует отдельный тип Optional или просто | None. Здесь также предпочтительно второе.
from typing import Optional
param_1: Optional[str]
param_2: str | None
Отдельно упомяну про тип Any из модуля typing. По сути он и означает то, чем является - любой тип данных. Например, если вы работаете с большим словарем с кучей вложенностей разных типов, можно просто прописать param: dict[str, Any]. Нет боли от прописывания всех типов + понятно, что работаешь с словарем. Конечно, без необходимости лучше избегать этот тип и явно прописывать то, с чем работаешь.
Возвращаемые значения
Здесь никакой магии, как и писал выше - просто указываем type hints через стрелочку:
def my_func(param1: int, param2: int) -> int:
return param1 + param2
def __init__(self, param: str) -> None:
self.param: str = param
Стоит упомянуть, что если в type hints прописан один тип данных и при этом вы работаете с другим - ошибок не будет, но IDE подсветит такие моменты.
Можно ли автоматически проверять типизацию?
Да. Для этого понадобится кое-что прикрутить к проекту. Чаще всего предпочтение отдается mypy или ruff. Так линтеры / чекеры будут стоять на страже качества кода и не придется уделять этому дополнительное внимание на код ревью.
Не бойтесь указывать аннотации типов где только возможно и мир станет чуточку лучше. :)
***
Присоединяйтесь ко мне в Telegram: https://t.me/python3_with_love. Там есть все, и читать код намного удобнее.