Что такое подсказки типов в Python 3.5?

Одна из самых обсуждаемых функций в Python 3.5 - это подсказки типа.

Пример подсказок типа упоминается в этой статье и этой, а также упоминается об ответственном использовании подсказок типа. Может кто-то объяснить больше о них, и когда они должны использоваться, а когда нет?

Ответ 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, типы подсказок обычно должны иметь три формы:

Кроме того, вы захотите использовать подсказки типа в сочетании с новым 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?, как было сказано ранее, для небольшого введения в них.

Ответ 2

Добавление к Джиму подробного ответа:

Проверьте модуль typing - этот модуль поддерживает подсказки типов, указанные в PEP 484.

Например, приведенная ниже функция принимает и возвращает значения типа str и аннотируется следующим образом:

def greeting(name: str) -> str:
    return 'Hello ' + name

Модуль typing также поддерживает:

Ответ 3

Недавно выпущенный PyCharm 5 поддерживает подсказки типов. В своем сообщении в блоге об этом (см. подсказка типа Python 3.5 в PyCharm 5) они предлагают отличное объяснение , какие типы намеков и не являются вместе с несколькими примерами и иллюстрациями, как использовать их в вашем коде.

Кроме того, он поддерживается в Python 2.7, как описано в этом комментарии:

PyCharm поддерживает модуль ввода из PyPI для Python 2.7, Python 3.2-3.4. Для 2.7 вам нужно поместить подсказки типа в *.pyi файлы-заглушки, поскольку в Python 3.0 были добавлены аннотации функций.