Type Hints Python

Содержание
Введение
Простой пример
Более сложные условия
typing
Стандартная библиотека
Объявление переменных
weakref
Похожие статьи

Введение

В соответствии с PEP 484 рекомендуется указывать ожидаемые типы данных как для параметров, так и для возвращаемого значения.

Официальный термин, который используется в англоязычной среде это Optional Type Annotations

Никаких ограничений в самом Python эти подсказки не накладывают. Можно спокойно выполнить код в котором типы не соответствуют указанным в подсказках.

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

Основные линтеры для Python кода это

Функции с подсказками о типах выглядят следующим оригинальным образом

def my_fun(s: str, factor: int) -> int: return s * factor

В подсказках типов можно использовать любые существующие типы данных, в том числе созданные пользователем. Пример с bool

def odd(n: int) -> bool: return n % 2 != 0

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

class MyClass: def __init__(self, value: float) -> None: self.value = value def get_value(obj: MyClass) -> bool: return int(obj.value) % 2 != 0

Значимость аннотации типов для Python сообщества значительно выросло с момента написания PEP 484. Успех Pydantic является одним из подтверждений спроса на проверку типов. В связи с этим был создан совет по типам - Typing Council, о котором можно прочитать в PEP 729

В комментариях

Самым первым способом создавать подсказки были комментарии. Никогда не видел такого в рабочих Python 3 проектах, видимо это был способ для Python 2

def square(x): # type: (float) -> float return x * x

Простой пример

В Python 3 аннотации типов создаются следующим оригинальным образом

# typehints.py def sum(first: float, second: float) -> float: return(first + second) print(sum(3.0, 4.6))

python typehints.py

7.6

Так как никаких ограничений в самом Python эти подсказки не накладывают. Можно спокойно выполнить

print(sum("a", "b"))

И получить

ab

Подсказки нужны скорее для сторонней проверки, каким-либо инструментом, например mypy.

python -m mypy ./typehints.py

typehints.py:7: error: Argument 1 to "sum" has incompatible type "str"; expected "float" [arg-type] typehints.py:7: error: Argument 2 to "sum" has incompatible type "str"; expected "float" [arg-type] Found 2 errors in 1 file (checked 1 source file)

Более сложные условия

Часто бывает полезно сделать подсказку на тип данных более сложным чем встроенный.

Например объект должен быть списком из целых чисел, а не любым списком. В версиях Python 3.5, 3.6, 3.7 и 3.8 использовали модуль typing, который появился в Python 3.5.

С версии 3.9 возможно создавать составные условия из стандартных типов без использования typing

typing

Официальная документация

from typing import List def printer(ls: List[int]) -> None: for elem in ls: print(elem) printer([1, 2])

В typing List пишется с большой буквы

python -m mypy .\typehints.py

Success: no issues found in 1 source file

Если теперь попробовать передать в функцию список со строкой mypy выдаст ошибку

printer([1, "a"])

python -m mypy .\typehints.py

typehints.py:8: error: List item 1 has incompatible type "str"; expected "int" [list-item] Found 1 error in 1 file (checked 1 source file)

Начиная с Python 3.9 этот способ указания стандартных типов является устаревшим, но всё ещё используется.

Не советую писать код таким способом в виду потенциальной потери поддержки в новых версиях Python

Насколько я понимаю использовать другие функции typing всё ещё можно но следует следить за тем что получает статус deprecated в документации

С помощью typing можно создавать аннотации и для более сложных типов. Например, нужен список, элементы которого могут быть как int так и str, но не float

from typing import List, Union list_elem = Union[int, str] def printer(ls: List[list_elem]) -> None: for elem in ls: print(elem) printer([1, 2.0])

python -m mypy typehints.py

typehints.py:11: error: List item 1 has incompatible type "float"; expected "int | str" [list-item] Found 1 error in 1 file (checked 1 source file)

float не подходит, а вот если заменить 2.0 на строку "abc" ошибки не будет

printer([1, "abc"])

python -m mypy typehints.py

Success: no issues found in 1 source file

Можно заранее создать кастомный тип my_list в котором разрешены целые числа и строки.

from typing import List, Union my_list = List[Union[int, str]] def printer(ls: my_list) -> None: for elem in ls: print(elem) printer([1, "abc"])

Для необязательных параметров в typing предусмотрено ключевое слово Optional

from typing import Optional def sum(a: int, b: int, c: Optional[int] = 100) -> int: if c: return a + b + c else: return a + b print(sum(1, 2)) print(sum(1, 2, 3))

python typehints.py

103 6

python -m mypy typehints.py

Success: no issues found in 1 source file

Можно создавать аннотации для словарей указывая разрешенные типы как ключей так и значений.

from typing import Dict, Union my_dict = Dict[str, Union[int, str]] def printer(dct: my_dict) -> None: for k, v in dct.items(): print(k, v) printer({"k": "v", "two": 2})

k v two 2

python -m mypy typehints.py

Success: no issues found in 1 source file

printer({"k": "v", "two": 2})

k v two 2.0

python -m mypy typehints.py

typehints.py:12: error: Dict entry 1 has incompatible type "str": "float"; expected "str": "int | str" [dict-item] Found 1 error in 1 file (checked 1 source file)

Стандартная библиотека

Начиная с Python 3.9 для составных подсказок можно использовать встроенные коллекции , например, обычный список

def printer(ls: list[int]) -> None: for elem in ls: print(elem) printer([1, 2])

python typehints.py

1 2

Если выполнив этот код через mypy вы получаете ошибку

python -m mypy .\typehints.py

typehints.py:1: error: "list" is not subscriptable, use "typing.List" instead [misc] Found 1 error in 1 file (checked 1 source file)

Значит у вас версия Python, в которой mypy не поддерживает обычные типы данных в составных подсказках - в PEP 585 support is broken. #9873 можно прочитать о задержке с поддержкой этой фичи. В 3.12 поддержка точно есть.

Часто бывает нужна аннотация на ещё более сложный тип. Например, нужен список, элементами которого могут быть как целые числа, так и словари.

Начиная с Python 3.10 можно воспользоваться оператором |. PEP 604

Убедимся, что без | mypy видит ошибку

def printer(ls: list[int]) -> None: for elem in ls: print(elem) printer([1, {"k": "v"}])

python -m mypy typehints.py

typehints.py:5: error: List item 1 has incompatible type "dict[str, str]"; expected "int" [list-item] Found 1 error in 1 file (checked 1 source file)

Добавим |

def printer(ls: list[int | dict]) -> None: for elem in ls: print(elem) printer([1, {"k": "v"}])

python -m mypy typehints.py

Success: no issues found in 1 source file

Можно пойти дальше и указать, что словарь должен быть вида строка:строка. Теперь если передать словарь вида str:int mypy выдаст ошибку.

def printer(ls: list[int | dict[str, str]]) -> None: for elem in ls: print(elem) printer([1, {"k": 3}])

python -m mypy .\typehints.py

typehints.py:5: error: Dict entry 0 has incompatible type "str": "int"; expected "str": "str" [dict-item] Found 1 error in 1 file (checked 1 source file)

В фреймворке FastAPI указание или не указание type hints позволяет накладывать реальные ограничения на передаваемые агрументы.

Type hints используются не только в функциях но и, например при объявлении dataclass

Переменные

Начиная с Python 3.6 можно создавать аннотации типов при объявлении переменных

x: int = 3 print(x * x)

python -m mypy .\typehints.py

Success: no issues found in 1 source file

Неверная аннотация будет проигнорирована интерпретатором, но замечена mypy

s: str = 1

python -m mypy .\typehints.py

typehints.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "str") [assignment] Found 1 error in 1 file (checked 1 source file)

mypy может понять, что тип используется неправильно. В данном примере можно и не использовать аннотацию, так как mypy поймёт его и так.

s: str = "abc" print(s * s)

python -m mypy .\typehints.py

typehints.py:2: error: Unsupported operand types for * ("str" and "str") [operator] Found 1 error in 1 file (checked 1 source file)

weakref

Если нужно сделать аннотацию типа для слабой ссылки нужно воспользоваться weakref.ReferenceType

self.data: weakref.ReferenceType["MyType"] = weakref.ref(data)

Optional

Рассмотрим функцию, которая возвращает либо строку либо None

def parity(value: int | str) -> str | None: try: value = int(value) except ValueError as e: print(e) return None else: if value % 2 == 0: return "even" else: return "odd" arg = "2" s: str = f"Parity of {arg} is " + parity(arg) print(s)

Parity of 2 is even

Если попытаться использовать возвращаемое значение из этой функции как строку - mypy выдаст предупреждение

src\t.py:14: error: Unsupported operand types for + ("str" and "None") [operator] src\t.py:14: note: Right operand is of type "str | None" Found 1 error in 1 file (checked 1 source file)

Потому что в случае нековертируемого аргумента строку придётся складывать с None и это приведёт к ошибке

arg = "A" s: str = f"{arg} is " + parity(arg) print(s)

Traceback (most recent call last): File "C:\Users\Andrei\src\t.py", line 14, in <module> s: str = f"{arg} is " + parity(arg) ~~~~~~~~~~~~~^~~~~~~~~~~~~ TypeError: can only concatenate str (not "NoneType") to str

В данном примере можно изменить саму функцию, чтобы она и при пойманном исключении возвращала строку, например, "unknown" но мы исходим из того, что менять код функции нельзя.

Исправить ситуацию без редактирования функции можно приведением результата к типу str

arg = "A" s: str = f"Parity of {arg} is " + str(parity(arg)) print(s)

invalid literal for int() with base 10: 'A' Parity of A is None

python -m mypy t.py

Success: no issues found in 1 source file

Либо можно воспользоваться дополнительной проверкой на тип

arg = "A" p: str = str(parity(arg)) if parity(arg) else "undefined" s: str = f"Parity of {arg} is " + p print(s)

К сожалению, чтобы mypy не ругался здесь тоже присутствует совершенно не нужное приведение к типу str

invalid literal for int() with base 10: 'A' Parity of A is undefined

python -m mypy t.py

Success: no issues found in 1 source file

Автор статьи: Андрей Олегович

Похожие статьи
Основы Python
Type Hints
__future__
configparser
Менеджер контекста
docstring
#!: Shebang
Объекты
Итерация
os
pathlib

РЕКЛАМА от Яндекса. Может быть недоступна в вашем регионе

Конец рекламы. Если там пусто считайте это рекламой моей телеги

Поиск по сайту

Подпишитесь на Telegram канал @aofeed чтобы следить за выходом новых статей и обновлением старых

Перейти на канал

@aofeed

Задать вопрос в Телеграм-группе

@aofeedchat

Контакты и сотрудничество:
Рекомендую наш хостинг beget.ru
Пишите на info@urn.su если Вы:
1. Хотите написать статью для нашего сайта или перевести статью на свой родной язык.
2. Хотите разместить на сайте рекламу, подходящую по тематике.
3. Реклама на моём сайте имеет максимальный уровень цензуры. Если Вы увидели рекламный блок недопустимый для просмотра детьми школьного возраста, вызывающий шок или вводящий в заблуждение - пожалуйста свяжитесь с нами по электронной почте
4. Нашли на сайте ошибку, неточности, баг и т.д. ... .......
5. Статьи можно расшарить в соцсетях, нажав на иконку сети: