Каковы подсказки типа в Python 3.5?

250

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

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

Vaulstein
источник
4
Вы должны взглянуть на PEP 484, который связан с официальным журналом изменений .
Стефан
1
@AvinashRaj: Хорошее обсуждение о выбросах происходит здесь
Vaulstein
1
Жаль, что сценарий использования C-API полностью игнорируется этим PEP 484, в частности, подсказками типов для Cython и Numba.
denfromufa
2
Близко связаны: что такое переменные аннотации в Python 3.6? ,
Димитрис Фасаракис Хиллиард

Ответы:

344

Я бы посоветовал прочитать PEP 483 и PEP 484 и посмотреть эту презентацию Гвидо о Type Hinting.

В двух словах : Подсказка типа буквально означает, что означают слова, вы намекаете на тип объекта (ов), который вы используете .

Из-за динамической природы Python вывод или проверка типа используемого объекта особенно сложны. Этот факт мешает разработчикам понять, что именно происходит в коде, который они не написали, и, что наиболее важно, в инструментах проверки типов, имеющихся во многих средах разработки [PyCharm, PyDev], которые ограничены из-за того, что у них нет никакого индикатора того, какого типа объекты. В результате они прибегают к попыткам вывести тип с (как упоминалось в презентации) около 50% успеха.


Чтобы взять два важных слайда из презентации Type Hinting:

Зачем вводить подсказки?

  1. Помогает контролерам типов: намекая на тип, который вы хотите, чтобы объект был средством проверки типов, можно легко обнаружить, например, если вы передаете объект с типом, который не ожидается.
  2. Помогает с документацией: третье лицо, просматривающее ваш код, будет знать, где и как его использовать, следовательно, как его использовать, не получая их TypeErrors.
  3. Помогает IDE разрабатывать более точные и надежные инструменты: Среды разработки будут лучше подходить для предложения подходящих методов, когда вы знаете тип вашего объекта. Возможно, в какой-то момент вы столкнулись с этим в какой-то IDE, попав во .всплывающие методы и атрибуты, которые не определены для объекта.

Зачем использовать статические шашки типа?

  • Найти ошибки раньше : это само собой разумеется, я считаю.
  • Чем больше ваш проект, тем больше он вам нужен : опять же, имеет смысл. Статические языки обеспечивают надежность и контроль, которых нет у динамических языков. Чем крупнее и сложнее ваше приложение, тем больше необходим вам контроль и предсказуемость (с точки зрения поведения).
  • Большие команды уже проводят статический анализ : я предполагаю, что это подтверждает первые два пункта.

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

Как правило , вам не нужно беспокоиться об этом и определенно не нужно его использовать (особенно в тех случаях, когда вы используете Python в качестве вспомогательного языка сценариев). Это должно быть полезно при разработке больших проектов, поскольку оно предлагает столь необходимую надежность, контроль и дополнительные возможности отладки .


Тип Подсказка с mypy :

Чтобы сделать этот ответ более полным, я думаю, что небольшая демонстрация была бы подходящей. Я буду использовать mypyбиблиотеку, которая вдохновляла Type Hints, поскольку они представлены в PEP. В основном это написано для тех, кто сталкивается с этим вопросом и задается вопросом, с чего начать.

Прежде чем я это сделаю, позвольте мне повторить следующее: PEP 484 ничего не применяет; это просто установление направления для аннотаций функций и предложение рекомендаций относительно того, как можно / нужно выполнять проверку типов. Вы можете комментировать свои функции и подсказывать столько вещей, сколько хотите; ваши сценарии будут работать независимо от наличия аннотаций, потому что сам Python их не использует.

В любом случае, как отмечено в 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)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? Как уже упоминалось, для небольшого вступления по этим.

Димитрис Фасаракис Хиллиард
источник
3
«Из-за высокой динамической природы Python вывод или проверка типа используемого объекта особенно сложны». Вы имеете в виду статическую проверку, верно?
bsam
53

Добавляем к сложному ответу Джима:

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

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

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

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

  1. Тип псевдонимов .
  2. Тип подсказки для функций обратного вызова .
  3. Generics - Абстрактные базовые классы были расширены для поддержки подписки для обозначения ожидаемых типов для элементов контейнера.
  4. Пользовательские универсальные типы . Пользовательский класс может быть определен как универсальный класс.
  5. Любой тип - Каждый тип является подтипом Любого.
Ани Менон
источник
26

Недавно выпущенный 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 .

tsvenson
источник
0

Подсказка к типу - это недавнее дополнение к динамическому языку, где на протяжении десятилетий люди давали такие простые соглашения, как венгерский (обозначение объекта с первой буквой b = boolian, c = символ, d = словарь, i = целое число, l = список, n = число , s = string, t = tuple) были не нужны, слишком громоздки, но теперь решили, что, подождите ... слишком много проблем с использованием языка (type ()) для распознавания объектов и наших модных IDE нужна помощь в выполнении чего-либо такого сложного, и что динамически назначаемые значения объектов в любом случае делают их совершенно бесполезными, в то время как простое соглашение об именах могло бы решить все это для любого разработчика одним взглядом.

Ноа Ф. СанЦорвуц
источник
Честно говоря, это больше похоже на разглагольствование, чем на ответ.
Димитрис Фасаракис Хиллиард
-1

Подсказки к типу предназначены для удобства сопровождения и не интерпретируются Python. В приведенном ниже коде строка def add(self, ic:int)не приводит к ошибке до следующей return...строки:

class C1:
    def __init__(self):
        self.idn = 1
    def add(self, ic: int):
        return self.idn + ic
    
c1 = C1()
c1.add(2)

c1.add(c1)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 5, in add
TypeError: unsupported operand type(s) for +: 'int' and 'C1'
 
Леон Чанг
источник