Я бы посоветовал прочитать PEP 483 и PEP 484 и посмотреть эту презентацию Гвидо о Type Hinting.
В двух словах : Подсказка типа буквально означает, что означают слова, вы намекаете на тип объекта (ов), который вы используете .
Из-за динамической природы Python вывод или проверка типа используемого объекта особенно сложны. Этот факт мешает разработчикам понять, что именно происходит в коде, который они не написали, и, что наиболее важно, в инструментах проверки типов, имеющихся во многих средах разработки [PyCharm, PyDev], которые ограничены из-за того, что у них нет никакого индикатора того, какого типа объекты. В результате они прибегают к попыткам вывести тип с (как упоминалось в презентации) около 50% успеха.
Чтобы взять два важных слайда из презентации Type Hinting:
Зачем вводить подсказки?
- Помогает контролерам типов: намекая на тип, который вы хотите, чтобы объект был средством проверки типов, можно легко обнаружить, например, если вы передаете объект с типом, который не ожидается.
- Помогает с документацией: третье лицо, просматривающее ваш код, будет знать, где и как его использовать, следовательно, как его использовать, не получая их
TypeErrors
.
- Помогает IDE разрабатывать более точные и надежные инструменты: Среды разработки будут лучше подходить для предложения подходящих методов, когда вы знаете тип вашего объекта. Возможно, в какой-то момент вы столкнулись с этим в какой-то IDE, попав во
.
всплывающие методы и атрибуты, которые не определены для объекта.
Зачем использовать статические шашки типа?
- Найти ошибки раньше : это само собой разумеется, я считаю.
- Чем больше ваш проект, тем больше он вам нужен : опять же, имеет смысл. Статические языки обеспечивают надежность и контроль, которых нет у динамических языков. Чем крупнее и сложнее ваше приложение, тем больше необходим вам контроль и предсказуемость (с точки зрения поведения).
- Большие команды уже проводят статический анализ : я предполагаю, что это подтверждает первые два пункта.
В качестве заключительного замечания к этому небольшому введению : это дополнительная функция, и, насколько я понимаю, она была введена для того, чтобы пожинать некоторые из преимуществ статической типизации.
Как правило , вам не нужно беспокоиться об этом и определенно не нужно его использовать (особенно в тех случаях, когда вы используете Python в качестве вспомогательного языка сценариев). Это должно быть полезно при разработке больших проектов, поскольку оно предлагает столь необходимую надежность, контроль и дополнительные возможности отладки .
Тип Подсказка с mypy :
Чтобы сделать этот ответ более полным, я думаю, что небольшая демонстрация была бы подходящей. Я буду использовать mypy
библиотеку, которая вдохновляла Type Hints, поскольку они представлены в PEP. В основном это написано для тех, кто сталкивается с этим вопросом и задается вопросом, с чего начать.
Прежде чем я это сделаю, позвольте мне повторить следующее: PEP 484 ничего не применяет; это просто установление направления для аннотаций функций и предложение рекомендаций относительно того, как можно / нужно выполнять проверку типов. Вы можете комментировать свои функции и подсказывать столько вещей, сколько хотите; ваши сценарии будут работать независимо от наличия аннотаций, потому что сам Python их не использует.
В любом случае, как отмечено в PEP, типы подсказок обычно должны принимать три формы:
Кроме того, вы захотите использовать подсказки типов в сочетании с новым typing
модулем, представленным в Py3.5
. В нем определены многие (дополнительные) ABC (абстрактные базовые классы), а также вспомогательные функции и декораторы для использования при статической проверке. Большинство ABCs
из collections.abc
них включены, но в Generic
форме, чтобы разрешить подписку (путем определения __getitem__()
метода).
Для тех, кто заинтересован в более подробном их объяснении, mypy documentation
он написан очень красиво и содержит много примеров кода, демонстрирующих / описывающих функциональность их средства проверки; это определенно стоит прочитать.
Функция аннотации и специальные комментарии:
Во-первых, интересно наблюдать за поведением, которое мы можем получить при использовании специальных комментариев. Специальные # type: type
комментарии могут быть добавлены во время назначения переменных, чтобы указать тип объекта, если он не может быть напрямую выведен. Простые назначения, как правило, легко выводятся, но другие, такие как списки (относительно их содержимого), не могут.
Примечание: если мы хотим использовать какую-либо производную от Containers
и должны указать содержимое для этого контейнера, мы должны использовать универсальные типы из typing
модуля. Они поддерживают индексацию.
# generic List, supports indexing.
from typing import List
# In this case, the type is easily inferred as type: int.
i = 0
# Even though the type can be inferred as of type list
# there is no way to know the contents of this list.
# By using type: List[str] we indicate we want to use a list of strings.
a = [] # type: List[str]
# Appending an int to our list
# is statically not correct.
a.append(i)
# Appending a string is fine.
a.append("i")
print(a) # [0, 'i']
Если мы добавим эти команды в файл и выполним их с нашим интерпретатором, все будет работать нормально и print(a)
просто печатает содержимое списка a
. Эти # type
комментарии были отброшены, рассматриваются как простые комментарии , которые не имеют никакой дополнительной смысловой нагрузки .
Запустив это с mypy
другой стороны, мы получим следующий ответ:
(Python3)jimmi@jim: mypy typeHintsCode.py
typesInline.py:14: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"
Указывает, что список str
объектов не может содержать int
, что, статически говоря, является звуком. Это может быть исправлено либо соблюдением типа a
и только добавлением str
объектов, либо изменением типа содержимого, a
чтобы указать, что любое значение является приемлемым (интуитивно выполняется List[Any]
после того, Any
как было импортировано из typing
).
Аннотации функций добавляются в форму param_name : type
после каждого параметра в сигнатуре вашей функции, а тип возвращаемого значения указывается с помощью -> type
нотации перед двоеточием конечной функции; все аннотации хранятся в __annotations__
атрибуте этой функции в удобной словарной форме. Используя тривиальный пример (который не требует дополнительных типов из typing
модуля):
def annotated(x: int, y: str) -> bool:
return x < y
annotated.__annotations__
Атрибут теперь имеет следующие значения:
{'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}
Если мы полный нуби, или мы знакомы с Py2.7
концепциями и, следовательно, не знаем, что TypeError
скрывается в сравнении annotated
, мы можем выполнить еще одну статическую проверку, поймать ошибку и избавить нас от некоторых проблем:
(Python3)jimmi@jim: mypy typeHintsCode.py
typeFunction.py: note: In function "annotated":
typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")
Помимо прочего, вызов функции с недопустимыми аргументами также будет пойман:
annotated(20, 20)
# mypy complains:
typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"
Они могут быть распространены практически на любой сценарий использования, а обнаруженные ошибки распространяются не только на базовые вызовы и операции. Типы, которые вы можете проверить, действительно гибкие, и я просто дал небольшой пик своего потенциала. Посмотрите на typing
модуль, PEP или mypy
документы, чтобы дать вам более полное представление о предлагаемых возможностях.
Заглушки:
Заглушки могут использоваться в двух разных взаимоисключающих случаях:
- Вам нужно ввести команду check для модуля, для которого вы не хотите напрямую изменять сигнатуры функций
- Вы хотите написать модули и иметь проверку типов, но дополнительно хотите отделить аннотации от контента.
Окурки (с расширением .pyi
) представляют собой аннотированный интерфейс модуля, который вы делаете / хотите использовать. Они содержат сигнатуры функций, которые вы хотите проверить типом, а тело функций отбрасывается. Чтобы почувствовать это, приведем набор из трех случайных функций в модуле с именем randfunc.py
:
def message(s):
print(s)
def alterContents(myIterable):
return [i for i in myIterable if i % 2 == 0]
def combine(messageFunc, itFunc):
messageFunc("Printing the Iterable")
a = alterContents(range(1, 20))
return set(a)
Мы можем создать заглушку randfunc.pyi
, в которую мы можем поместить некоторые ограничения, если захотим. Недостатком является то, что кто-то, просматривающий источник без заглушки, на самом деле не получит помощь при аннотации, пытаясь понять, что и где предполагается передавать.
В любом случае, структура заглушки довольно проста: добавьте все определения функций с пустыми телами ( pass
заполненными) и предоставьте аннотации в соответствии с вашими требованиями. Здесь, давайте предположим, что мы хотим работать только с int
типами для наших Контейнеров.
# Stub for randfucn.py
from typing import Iterable, List, Set, Callable
def message(s: str) -> None: pass
def alterContents(myIterable: Iterable[int])-> List[int]: pass
def combine(
messageFunc: Callable[[str], Any],
itFunc: Callable[[Iterable[int]], List[int]]
)-> Set[int]: pass
combine
Функция дает представление о том , почему вы можете захотеть использовать аннотации в другом файле, они несколько раз не загромождать код и не снижают читаемость (большой нет-нет для Python). Конечно, вы можете использовать псевдонимы типов, но это иногда сбивает с толку больше, чем помогает (поэтому используйте их с умом).
Это должно познакомить вас с основными понятиями подсказок типов в Python. Несмотря на то, что используется средство проверки типов,
mypy
вы должны постепенно начать видеть больше всплывающих окон, некоторые из них внутри IDE ( PyCharm ,) и другие как стандартные модули Python. Я попытаюсь добавить дополнительные контрольные / связанные пакеты в следующем списке, когда и если я их найду (или, если предложено).
Шашки, которые я знаю :
- Mypy : как описано здесь.
- PyType : Google использует другую нотацию из того, что я собираю, вероятно, стоит посмотреть.
Связанные пакеты / проекты :
- typeshed: Официальное репозиторий Python, в котором размещены файлы-заглушки для стандартной библиотеки.
typeshed
Проект на самом деле один из лучших мест , где вы можете посмотреть , чтобы увидеть , как тип намекая может быть использован в проекте самостоятельно. Давайте возьмем в качестве примера в __init__
dunders этого Counter
класса в соответствующем .pyi
файле:
class Counter(Dict[_T, int], Generic[_T]):
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, Mapping: Mapping[_T, int]) -> None: ...
@overload
def __init__(self, iterable: Iterable[_T]) -> None: ...
Где _T = TypeVar('_T')
используется для определения общих классов . Для Counter
класса мы можем видеть, что он может не принимать аргументы в своем инициализаторе, получать единственный Mapping
тип из любого типа int
или принимать Iterable
любой тип.
Обратите внимание : я забыл упомянуть, что typing
модуль был введен на временной основе . От ОПТОСОЗ 411 :
Временный пакет может иметь свой API, модифицированный до «перехода» в «стабильное» состояние. С одной стороны, это состояние обеспечивает пакет преимуществами формальной части дистрибутива Python. С другой стороны, основная команда разработчиков прямо заявляет, что никаких обещаний относительно стабильности API пакета, которые могут измениться в следующем выпуске, не делается. Хотя это считается маловероятным результатом, такие пакеты могут быть даже удалены из стандартной библиотеки без периода устаревания, если опасения относительно их API или обслуживания окажутся обоснованными.
Так что возьмите вещи здесь с щепоткой соли; Я сомневаюсь, что это будет удалено или изменено значительными способами, но никто никогда не может знать.
** Еще одна тема, но действительная в контексте подсказок типов: PEP 526
Синтаксис для аннотаций переменных - это попытка заменить # type
комментарии введением нового синтаксиса, который позволяет пользователям аннотировать тип переменных в простых varname: type
выражениях.
Смотрите Что такое переменные аннотации в Python 3.6? Как уже упоминалось, для небольшого вступления по этим.