Ответ 1
Я бы предложил прочитать PEP 483 и PEP 484 и наблюдая эту презентацию Guido о типе Hinting.
В двух словах: Тип намека - это буквально то, что означают слова, вы намекаете на тип объекта (ов), который вы используете.
Из-за динамического характера Python особенно сложно определить или использовать тип используемого объекта. Этот факт затрудняет понимание разработчиками того, что именно происходит в коде, который они не написали, и, что самое важное, для инструментов проверки типов, обнаруженных во многих IDE [PyCharm, PyDev приходят на ум], которые ограничены из-за того, что у них нет индикатора того, какими являются объекты. В результате они прибегают к попытке вывести тип с (как указано в презентации) примерно на 50%.
Чтобы взять два важных слайда из презентации Type Hinting:
Почему введите подсказки?
- Помогает проверять тип:. Подчеркнув, какой тип вы хотите, чтобы объект был контролером типа, можно легко обнаружить, например, если вы передаете объект с типом, который не ожидается.
- Помогает с документацией: Третий человек, просматривающий ваш код, будет знать, что ожидается где, ergo, как его использовать, не получая их
TypeErrors
. - Помогает IDE разрабатывать более точные и надежные инструменты:. Среда разработки лучше подходит для того, чтобы предлагать соответствующие методы, когда знаете, какой тип вашего объекта. Вероятно, вы испытали это с некоторой IDE в какой-то момент, нажав
.
и обнаружив при этом методы/атрибуты, которые не определены для объекта.
Зачем использовать Static Type Checkers?
- Найти ошибки раньше. Я считаю, это очевидно.
- Чем больше ваш проект, тем больше вам нужно. Опять же, имеет смысл. Статические языки обеспечивают надежность и динамических языков. Чем больше и сложнее ваше приложение, тем больше контроля и предсказуемости (от поведенческий аспект), который вам нужен.
- Крупные команды уже запускают статический анализ. Я предполагаю, что это проверяет первые две точки.
В качестве заключительной заметки для этого небольшого введения: это необязательная функция, и, насколько я понимаю, она была введена для получения некоторых преимуществ статической типизации.
Вам обычно не нужно беспокоиться об этом, а определенно не нужно использовать его (особенно в тех случаях, когда вы используете Python в качестве вспомогательного языка сценариев). Это должно быть полезно при разработке крупных проектов, поскольку оно предлагает столь необходимую надежность, контроль и дополнительные возможности отладки.
Тип Hinting с помощью mypy:
Чтобы сделать этот ответ более полным, я думаю, что небольшая демонстрация будет подходящей. Я буду использовать mypy
, библиотеку, которая вдохновила Type Hints, поскольку они представлены в PEP. Это в основном написано для любого, кто сталкивается с этим вопросом и задается вопросом, с чего начать.
Прежде чем я это сделаю, позвольте мне повторить следующее: PEP 484 ничего не обеспечивает; он просто устанавливает направление для функции аннотации и рекомендации для , как проверка типа может/должна выполняться. Вы можете аннотировать свои функции и подскажите как можно больше вещей; ваши скрипты будут работать независимо от присутствия аннотаций, потому что сам Python их не использует.
В любом случае, как указано в PEP, типы подсказок обычно должны иметь три формы:
- Функциональные аннотации. (PEP 3107)
- Файлы-заглушки для встроенных/пользовательских модулей.
- Специальные
# type: type
комментарии, которые дополняют первые две формы. (См. Что представляют собой переменные аннотации в Python 3.6? для обновления Python 3.6 для комментариев# type: type
)
Кроме того, вы захотите использовать подсказки типа в сочетании с новым 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)[email protected]: 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'>}
Если мы являемся полным noobie или знакомы с концепциями Py2.7
и, следовательно, не знаем о TypeError
, скрывающемся при сравнении annotated
, мы можем выполнить еще одну статическую проверку, уловить ошибку и сохранить нас некоторые проблемы:
(Python3)[email protected]: 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
предоставят вам более полное представление о предлагаемых возможностях.
Файлы-заглушки:
Файлы-заглушки могут использоваться в двух разных взаимоисключающих случаях:
- Вам нужно ввести проверку модуля, для которого вы не хотите напрямую изменять сигнатуры функций
- Вы хотите писать модули и иметь проверку типов, но дополнительно хотите отделить аннотации от содержимого.
Какие файлы-заглушки (с расширением .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). Разумеется, вы можете использовать псевдонимы типов, но что-то смущает больше, чем это
помогает (поэтому используйте их с умом).
Это должно помочь вам ознакомиться с базовыми понятиями Type Hints в Python. Несмотря на то, что используемый
mypy
вам следует постепенно начинать видеть, что их всплывают, некоторые внутри IDE ( PyCharm) и другие в качестве стандартных модулей python.
Я попытаюсь добавить дополнительные шашки/связанные пакеты в следующий список, когда и если я их найду (или, если это предложение).
Шашки, которые я знаю:
- Mypy: как описано здесь.
- PyType: Google использует разные обозначения из того, что я собираюсь, возможно, стоит посмотреть.
Связанные пакеты/проекты:
- typeshed: Официальное репозиторинг Python содержит набор файлов-заглушек для стандартной библиотеки.
Проект typeshed
на самом деле является одним из лучших мест, где вы можете посмотреть, как тип намека может быть использован в собственном проекте. Возьмем в качестве примера dunters __init__
класса 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
был представлен на временной основе. От PEP 411:
Предварительный пакет может изменить свой API до "перехода" в "стабильное" состояние. С одной стороны, это состояние предоставляет пакет преимуществам формальной части дистрибутива Python. С другой стороны, основная команда разработчиков явно заявляет, что no promises сделаны в отношении стабильности API-интерфейса пакета, которые могут измениться для следующей версии. Хотя это считается маловероятным результатом, такие пакеты могут даже быть удалены из стандартной библиотеки без периода устаревания, если проблемы, связанные с их API или обслуживанием, будут обоснованными.
Так что делайте здесь здесь щепотку соли; Я сомневаюсь, что это будет удалено или изменено значительными способами, но никогда не может быть известно.
** Еще одна тема, но действительная в рамках типов-подсказок: PEP 526
: Синтаксис для переменных аннотаций пытается заменить комментарии # type
, введя новый синтаксис, который позволяет пользователям аннотировать тип переменных в простых операциях varname: type
.
См. Что представляют собой переменные аннотации в Python 3.6?, как было сказано ранее, для небольшого введения в них.