Аннотированная переменная типа Python 3.5 без начального значения - PullRequest
1 голос
/ 13 июня 2019

Мне нужно объявить глобальные переменные, которые имеют «сложный» тип, и не следует создавать экземпляр во время импорта. В Python 3.6+ я могу опустить инициализацию, например:

log: logging.Logger
pollset: select.poll

Мне нужно сделать код совместимым с Python 3.5. Я могу использовать комментарии типа комментария:

log = ...  # type: logging.Logger
pollset = ...  # type: select.poll

но тогда я должен предоставить начальное значение. Это не проблема во время выполнения, подойдет начальное значение None или .... Но любой из них вызывает ошибку проверки типа: «1011»

myprog.py:19: error: Incompatible types in assignment (expression has type "ellipsis", variable has type "Logger")

Конечно, я мог бы использовать тип Optional, чтобы разрешить инициализацию до None, но тогда проверка типов будет ослаблена. Например, присвоение значения None переменной в другом месте кода недопустимо, но оно не будет перехвачено.

Есть ли общепринятый способ использовать строгую проверку типов для переменной, совместимой с Python 3.5?

Ответы [ 2 ]

1 голос
/ 13 июня 2019

Согласно PEP 484 назначение None является правильным.

log = None  # type: logging.Logger

Обратите внимание, что mypy допускает это только в области видимости класса. Однако вы можете объявить тип и , чтобы указать mypy игнорировать само присвоение (, начиная с mypy 0.700 ).

log = None  # type: logging.Logger  # type: ignore

Кроме того, вы можете использовать заглушку .pyi независимо от версии Python.

# my_lib.pyi
log: logging.Logger

Однако в коде, не являющемся заглушкой, для версий Python 3.5 и более ранних версий существует особый случай:

from typing import IO

stream = None  # type: IO[str]

Проверяющие типа не должны жаловаться на это (несмотря на то, что значение None не соответствует данному типу), и при этом они не должны изменять выводимый тип на Необязательный [...] (несмотря на правило, которое делает это для аннотированных аргументов со значением по умолчанию). Нет). Здесь предполагается, что другой код будет гарантировать, что переменной присвоено значение правильного типа, и все пользователи могут предполагать, что переменная имеет данный тип.


1 голос
/ 13 июня 2019

Один из методов, который вы могли бы сделать, - создать фиктивную переменную с типом Any, а затем использовать ее вместо установки переменных ... или None.Например:

from typing import Any

_bogus = None     # type: Any
log = _bogus      # type: logging.Logger
pollset = _bogus  # type: select.poll

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

Однако при таком подходе мы вместо этого получимNone, что противоречит нашему объявленному типу.

Может быть, это нормально для вашего варианта использования, но если это не так, мы можем приблизиться к поведению аннотаций переменных, вставив их внутрь if TYPE_CHECKING block:

from typing import Any, TYPE_CHECKING

if TYPE_CHECKING:
    _bogus = None     # type: Any
    log = _bogus      # type: logging.Logger
    pollset = _bogus  # type: select.poll

Переменная TYPE_CHECKING всегда имеет значение False во время выполнения, но обрабатывается, как если бы она была True, средствами проверки типов, такими как mypy.

(Doing if False также работаетЭто достаточно распространенное соглашение, что mypy напрямую поддерживает это как альтернативу использованию TYPE_CHECKING.)

...