В разделе часто задаваемых вопросов говорится о производительности вашего кода Python.
В некоторых языках программирования подсказки типов могут помочь компилятору точно в срок ускорить компиляцию подсказанного кода и тем самым повысить производительность. В Python это не так, языковая среда выполнения не использует подсказки типов, которые рассматриваются как не что иное, как метаданные.
Минимальное влияние на производительность дает дополнительный байт-код, необходимый для запуска подсказки. определения (импорт, TypeVar
назначения и интерпретация самих аннотаций). Это воздействие действительно минимально, даже при повторном создании классов и функций.
Вы можете сделать воздействие видимым, используя подсказки типов в коде, выполняемом через exec()
;это крайний случай, когда мы добавляем намного больше накладных расходов в код, который делает очень мало:
>>> import timeit
>>> without_hints = compile("""def foo(bar): pass""", "", "exec")
>>> with_hints = compile(
... "from typing import List\ndef foo(bar: List[int]) -> None: pass",
... "", "exec")
>>> without_metrics = timeit.Timer('exec(s)', 'from __main__ import without_hints as s').autorange()
>>> with_metrics = timeit.Timer('exec(s)', 'from __main__ import with_hints as s').autorange()
>>> without_metrics[1] / without_metrics[0] * (10e6)
4.217094169580378
>>> with_metrics[1] / with_metrics[0] * (10e6) # microseconds per execution
19.113581199781038
Таким образом, добавление подсказок типа добавляет ~ 15 микросекунд времени выполнения, так как Python должен импортировать объект List
из typing
и прикрепите подсказки к созданному функциональному объекту.
15 микросекунд минимален для всего, что определено на верхнем уровне модуля, который необходимо импортировать только один раз.
Вы можете увидеть это, когда разберете сгенерированный байт-код. Сравните версию без подсказок:
>>> dis.dis(without_hints)
1 0 LOAD_CONST 0 (<code object foo at 0x10ace99d0, file "<dis>", line 1>)
2 LOAD_CONST 1 ('foo')
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (foo)
8 LOAD_CONST 2 (None)
10 RETURN_VALUE
Disassembly of <code object foo at 0x10ace99d0, file "<dis>", line 1>:
1 0 LOAD_CONST 0 (None)
2 RETURN_VALUE
с версией, на которую намекают:
>>> import dis
>>> dis.dis(with_hints)
1 0 LOAD_CONST 0 (0)
2 LOAD_CONST 1 (('List',))
4 IMPORT_NAME 0 (typing)
6 IMPORT_FROM 1 (List)
8 STORE_NAME 1 (List)
10 POP_TOP
2 12 LOAD_NAME 1 (List)
14 LOAD_NAME 2 (int)
16 BINARY_SUBSCR
18 LOAD_CONST 2 (None)
20 LOAD_CONST 3 (('bar', 'return'))
22 BUILD_CONST_KEY_MAP 2
24 LOAD_CONST 4 (<code object foo at 0x10ace99d0, file "<dis>", line 2>)
26 LOAD_CONST 5 ('foo')
28 MAKE_FUNCTION 4 (annotations)
30 STORE_NAME 3 (foo)
32 LOAD_CONST 2 (None)
34 RETURN_VALUE
Disassembly of <code object foo at 0x10ace99d0, file "<dis>", line 2>:
2 0 LOAD_CONST 0 (None)
2 RETURN_VALUE
Представлен Python 3.7 PEP 563 - отложенная оценка аннотаций , нацеленнаяпри небольшом уменьшении этой стоимости и облегчении использования прямых ссылок. Для приведенного выше упрощенного примера это на самом деле не сокращает время, затрачиваемое на загрузку предопределенных аннотаций, также занимает некоторое время:
>>> pep563 = compile(
... "from __future__ import annotations\nfrom typing import List\ndef foo(bar: List[int]) -> None: pass",
... "", "exec")
>>> pep563_metrics = timeit.Timer('exec(s)', 'from __main__ import pep563 as s').autorange()
>>> pep563_metrics[1] / pep563_metrics[0] * (10e6) # microseconds per execution
19.314851402305067
, но для более сложных, реальных проектов хинтинга типов это делаетсделать небольшую разницу.