Импорт from __future__ import annotations
имеет одно основное преимущество: он позволяет использовать прямые ссылки очиститель.
Например, рассмотрим эту (в настоящее время неработающую) программу.
# Error! MyClass has not been defined yet in the global scope
def foo(x: MyClass) -> None:
pass
class MyClass:
# Error! MyClass is not defined yet, in the class scope
def return_copy(self) -> MyClass:
pass
Эта программа на самом деле вылетает , когда вы пытаетесь запустить ее во время выполнения: вы пытались использовать 'MyClass' до того, как он вообще когда-либо будет определен.Чтобы исправить это раньше, вам пришлось либо использовать синтаксис комментариев типа, либо обернуть каждый 'MyClass' в строку, чтобы создать прямую ссылку :
def foo(x: "MyClass") -> None:
pass
class MyClass:
def return_copy(self) -> "MyClass":
pass
Хотя это работаетЧувствую себя очень вяло.Типы должны быть типами: нам не нужно вручную преобразовывать определенные типы в строки только для того, чтобы типы хорошо воспроизводились во время выполнения Python.
Мы можем исправить это, добавив from __future__ import annotations
import: эту строкуавтоматически заставляет все печатать строку во время выполнения.Это позволяет нам писать код, который выглядит как первый пример, без сбоев: поскольку каждый тип подсказки является строкой во время выполнения, мы больше не ссылаемся на то, что еще не существует.
И проверки типов, такие как mypyили Pycharm не будет заботиться: для них тип выглядит одинаково независимо от того, как Python сам выбирает его для представления.
Следует отметить, что этот импорт сам по себе не позволяетИзбегайте импорта вещей.Это просто делает его чище при этом.
Например, рассмотрим следующее:
from expensive_to_import_module import MyType
def blah(x: MyType) -> None: ...
Если expensive_to_import_module
выполняет много логики запуска, это может означать, что он не требуетнезначительное количество времени для импорта MyType
.Это не будет иметь большого значения, когда программа действительно будет запущена, но делает , замедляя время запуска.Это может быть особенно неприятно, если вы пытаетесь написать недолговечные программы в стиле командной строки: добавление подсказок типа иногда может заставить вашу программу чувствовать себя более вялой при запуске.
Мы могли бы исправьте это, сделав MyType
строку, скрывая импорт за if TYPE_CHECKING
охранником, например:
from typing import TYPE_CHECKING
# TYPE_CHECKING is False at runtime, but treated as True by type checkers.
# So the Python interpreters won't do the expensive import, but the type checker
# will still understand where MyType came from!
if TYPE_CHECKING:
from expensive_to_import_module import MyType
# This is a string reference, so we don't attempt to actually use MyType
# at runtime.
def blah(x: "MyType") -> None: ...
Это работает, но снова выглядит неуклюже.Зачем нам нужно добавлять кавычки вокруг последнего типа?annotations
импорт в будущем делает синтаксис для этого немного более плавным:
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from expensive_to_import_module import MyType
# Hooray, no more quotes!
def blah(x: MyType) -> None: ...
В зависимости от вашей точки зрения, это может не показаться огромным выигрышем.Но это делает намного более эргономичным использование подсказок типов, заставляет их чувствовать себя более «интегрированными» в Python, и (как только они становятся включенными по умолчанию) удаляет общий камень преткновения для новичков в PEP 484, как правило, опрокидывается,