Які підказки типу в Python 3.5?


250

Однією з найбільш обговорюваних функцій в Python 3.5 є підказки типу .

Приклад натяків типу згадується в цій статті , і це одне в той же час , згадуючи , щоб використовувати підказки типу відповідально. Чи може хтось пояснити більше про них і коли їх слід використовувати, а коли ні?


4
Ви можете подивитися на PEP 484, який пов'язаний з офіційного журналу змін .
Стефан

1
@AvinashRaj: Гарне обговорення про викиди відбувається тут
Vaulstein

1
Шкода, що цей PEP 484 повністю ігнорує випадок використання C-API, особливо типові підказки для Cython та Numba.
denfromufa

Відповіді:


343

Я б запропонував прочитати PEP 483 та PEP 484 і переглянути цю презентацію від Guido щодо натяку на тип.

Коротше кажучи : підказка типу - це буквально те, що означають слова, ви натякаєте на тип об’єктів, які ви використовуєте .

Зважаючи на динамічний характер Python, особливо важко зробити висновок або перевірити тип використовуваного об'єкта. Цей факт розробникам важко зрозуміти, що саме відбувається в коді, який вони не написали, і, що найголовніше, для інструментів перевірки типів, знайдених у багатьох ІДЕ [PyCharm, PyDev приходять на думку], які обмежені через те, що у них немає жодного показника того, який тип об’єктів є. Як результат, вони вдаються до спроб зробити висновок про тип (як згадується у презентації) приблизно 50% успішності.


Щоб взяти два важливі слайди з презентації Type Hinting:

Чому введіть підказки?

  1. Допомагає перевірки типів : натякаючи на тип, який ви хочете, щоб об’єкт був перевіряючим типом, можна легко виявити, якщо, наприклад, ви передаєте об'єкт типу, який не очікується.
  2. Допомога з документацією: Третя особа, яка переглядає ваш код, буде знати, що очікується, де, ерго, як ним користуватися, не отримуючи їх TypeErrors.
  3. Допомагає ІДЕ розробити більш точні та надійні інструменти: Середовища розробки будуть більш підходящими для пропонування відповідних методів, коли знати, що тип вашого об’єкта. Ви, мабуть, відчували це з деяким IDE в якийсь момент, потрапляючи на .і маючи методи / атрибути спливаючих, які не визначені для об'єкта.

Навіщо використовувати шашки статичного типу?

  • Швидше знайдіть помилок : я вважаю, це зрозуміло.
  • Чим більший ваш проект, тим більше вам потрібно : знову ж таки, має сенс. Статичні мови пропонують надійність і контроль, яких не вистачає динамічним мовам. Чим більша і складніша ваша програма, тим більше контролю і передбачуваності (з поведінкового аспекту) вам потрібно.
  • Великі команди вже проводять статичний аналіз : я думаю, це підтверджує перші два моменти.

Як заключна примітка до цього невеликого вступу : Це необов'язкова функція, і, наскільки я розумію, вона була введена для того, щоб скористатись деякими перевагами статичного набору тексту.

Зазвичай вам не потрібно про це турбуватися, і, безумовно , не потрібно його використовувати (особливо у випадках, коли ви використовуєте Python як допоміжну мову сценарію). Це має бути корисним при розробці великих проектів, оскільки він пропонує дуже потрібну надійність, контроль та додаткові можливості налагодження .


Тип Підказка з mypy :

Для того, щоб зробити цю відповідь більш повною, я думаю, що невелика демонстрація була б підходящою. Я буду використовувати mypyбібліотеку, яка надихнула підказки типу, коли вони представлені в PEP. Це в основному написано для тих, хто стикається з цим питанням і цікавиться, з чого почати.

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

У будь-якому випадку, як зазначається в ПЕП, типи натяків, як правило, мають три форми:

  • Анотації до функцій. ( PEP 3107 )
  • Файли заготовки для вбудованих / користувальницьких модулів.
  • Спеціальні # type: typeкоментарі, які доповнюють перші дві форми. (Див.: Що таке змінні анотації в Python 3.6? Для оновлення Python 3.6 для # type: typeкоментарів)

Крім того, ви хочете використовувати підказки типу в поєднанні з новим typingмодулем, введеним в Py3.5. У ньому багато (додаткові) АВС (абстрактні базові класи) визначені разом із допоміжними функціями та декораторами для використання у статичній перевірці. Більшість 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модулі, PEPs або в mypyдокументах дасть вам більш повне уявлення про пропоновані можливості.

Файли заглушки:

Файли Stub можна використовувати у двох різних не виключають один одного випадках:

  • Вам потрібно набрати перевірку модуля, для якого ви не хочете безпосередньо змінювати підписи функцій
  • Ви хочете писати модулі та перевіряти тип, але додатково хочете відокремлювати анотації від контенту.

Файли заглушок (з розширенням .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. Я спробую додати додаткові шашки / пов'язані пакети у наступний список, коли і якщо я їх знайду (або якщо вони запропоновані).

Шашки, які я знаю :

  • Міпі : як описано тут.
  • PyType : Google використовує різні позначення від того, що я збираю, ймовірно, варто переглянути.

Пов'язані пакети / проекти :

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модуль був представлений на тимчасовій основі . Від PEP 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. Загальна інформація - Абстрактні базові класи були розширені, щоб підтримувати підписку для позначення очікуваних типів елементів контейнера.
  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 stub, оскільки анотації до функцій були додані в Python 3.0 .


0

Підказки типу - це недавнє доповнення до динамічної мови, де десятиліттями люди присягали називати конвенції такими ж простими, як угорська (мітка об'єкта з першою літерою b = булева, 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'
 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.