Согласно части PEP 484 «Использование None»:
При использовании в подсказке типа выражение
None
считается эквивалентнымtype(None)
.
Однако я столкнулся со случаем, когда оба не кажутся эквивалентными:
from typing import Callable, NamedTuple, Type, Union
# I define a set of available return types:
ReturnType = Union[
int,
None,
]
# I use this Union type to define other types, like this callable type.
SomeCallableType = Callable[..., ReturnType]
# But I also want to store some functions metadata (including the function's return type) in a `NamedTuple`:
class FuncInfos(NamedTuple):
return_type: Type[ReturnType]
# This works fine:
fi_1 = FuncInfos(return_type=int)
# But this issues an error:
# main.py:21: error: Argument "return_type" to "FuncInfos" has incompatible type "None"; expected "type[int] | type[None]" [arg-type]
# Found 1 error in 1 file (checked 1 source file)
fi_2 = FuncInfos(return_type=None)
# But this works fine:
fi_3 = FuncInfos(return_type=type(None))
Для меня не составляет особых проблем написать type(None)
, а не просто None
, но мне бы хотелось понять приведенную выше ошибку, которая, похоже, противоречит цитате из PEP 484.
Фрагмент доступен для исполнения здесь.
Обновлено: На самом деле кажется, что это сводится к следующему:
from typing import Type
a: Type[None]
# This seems to cause an issue:
# main.py:4: error: Incompatible types in assignment (expression has type "None", variable has type "type[None]") [assignment]
# Found 1 error in 1 file (checked 1 source file)
a = None
# This seems to work:
a = type(None)
Фрагмент доступен для исполнения здесь.
@MilesBudnek Разве следующий : Type[None]
return_type
не все еще является подсказкой типа? Просто None
и type(None)
— это два значения разных типов, оба нельзя использовать как взаимозаменяемые?
Type[None]
и type(None)
— это не одно и то же. Как сказано в предложении, которое вы процитировали, None
в подсказке типа эквивалентно type(None)
, поэтому Type[None]
эквивалентно Type[type(None)]
. Это означает, что a
в вашем редактировании является типом (в частности NoneType
), а None
не является типом.
Отвечает ли это на ваш вопрос? Подсказка типа Python 3 для None?
Я бы сверился с актуальными спецификациями типизации , но даже в этом случае формулировка при использовании в подсказке типа выражение None
считается эквивалентным type(None)
довольно неясно. Конечно, type(None)
никогда не может использоваться в подсказке типа — это выражение вызова (и, следовательно, не выражение аннотации).
Да, это было интересно.
Я бы сказал, что все сводится к следующему: None
на самом деле является экземпляром NoneType
, но для практических целей его следует рассматривать как None. Учтите следующее:
from types import NoneType
x: None = None # succeeds
y: NoneType = None # succeeds in pyright, fails in mypy
z: type[None] = None # fails in both
Странно, правда? Второй случай проходит по пирайту, поскольку в нем говорится, что None является экземпляром NoneType, но в mypy специальный случай явно запрещен: «NoneType не следует использовать в качестве типа, вместо этого используйте None»
Кажется, что когда вы определяете союз, который использует None, он предполагает, что type[None]
является частью союза:
from typing import reveal_type
x = int | None
y = None
reveal_type(x) # type[int] | type[None]
reveal_type(y) # None
Мне это кажется ошибкой — я ожидал, что show_type на x покажет:
reveal_type(x) # type[int] | None
...поскольку, как показано выше, type[None] не совсем канонический тип. Но это то, что есть, поэтому нам пока придется с этим работать.
Мне пришло в голову, что инспекция уже реализует подобную функцию, поэтому я бы посмотрел, какие типы имеют следующие:
from inspect import signature
def test() -> int:
return 1
x = signature(test)
print(x.return_annotation) # int
def test2() -> None:
pass
y = signature(test2)
print(y.return_annotation) # None
Оказывается, это не поддерживает статическую типизацию, но правильный тип для этих аннотаций возврата будет type[int] | None
. Лично я бы выделил «Нет», если это возможно:
from typing import Callable, NamedTuple
ReturnType = int | str
SomeCallableType = Callable[..., None | ReturnType]
class FuncInfos(NamedTuple):
return_type: None | type[ReturnType]
fi_1 = FuncInfos(return_type=int)
fi_2 = FuncInfos(return_type=None)
Надеюсь это поможет!
Здравствуйте и спасибо за этот ответ. Часть 1. Пример 2: Похоже, меня мотивировала эта проблема . Я действительно не понимал, насколько семантически (я не знаю о реализации на тот момент) , предоставленный mre, был проблемой.
В остальном я действительно нашел лучшее объяснение, чем объяснение из PEP484, которое лучше объясняет это поведение, из предыдущего Прагматики PEP (483) : «Там, где ожидается тип, тип (None) можно заменить None». ; например, Union[t1, None] == Union[t1, type(None)]."
Похоже, это указывает на то, что во всех приведенных выше примерах, когда он указан в месте, где ожидается тип, None
интерпретируется как type(None)
(NoneType
). Итак, из моего второго MRE: a: Type[None] = None
интерпретируется как a: Type[type(None)] = None
. Это означает, что a ожидает подклассы type(None)
/NoneType
, а не значения типа NoneType
. PEP 484 может быть просто слишком резко сформулирован, поскольку None
и type(None)
точно не будут эквивалентны.
Как показано в сообщении, в PEP 484 говорится что-то двусмысленное:
При использовании в подсказке типа выражение None считается эквивалентным type(None).
Но я узнал о PEP 483, который использует гораздо более четкую формулировку в своей Прагматике:
Если ожидается тип,
None
можно заменить наtype(None)
; напримерUnion[t1, None] == Union[t1, type(None)]
.
Учитывая это, различные примеры теперь начинают иметь смысл.
Например, мой второй MRE:
from typing import Type
a: Type[None]
# This seems to cause an issue:
# main.py:4: error: Incompatible types in assignment (expression has type "None", variable has type "type[None]") [assignment]
# Found 1 error in 1 file (checked 1 source file)
a = None
# This seems to work:
a = type(None)
теперь можно интерпретировать как:
from typing import Type
a: Type[type(None)] # Type[...] expects a type.
a = None
a = type(None)
Теперь тип a
явно является «подтипом type(None)
», что точно не соответствует типу выражения None
, отсюда и сообщение об ошибке.
Однако на самом деле это тип второго выражения: type(None)
(«Каждый тип является подтипом самого себя». Согласно самому PEP 483).
Итак, как было предложено в некоторых комментариях, я в основном неверно истолковал PEP 484 и забыл, что «эквивалентность» действует только «в подсказке типа» (или «там, где ожидается тип»). Чего не было при передаче параметров или назначении в моих фрагментах.
Кроме того, Type[None]
(подтип None
) не имел бы особого смысла, поскольку None
не является типом (отмените связь NoneType
), даже несмотря на то, что MyPy использует формулировку «тип None» (я бы ожидал чего-то вроде «None имеет тип NoneType», а не «None имеет тип None»).
«При использовании в подсказке типа»
return_type
не является подсказкой типа, это переменная класса, которая содержит тип.None
обычно не является типом; это объект (единственный объект) типаNoneType
. Только в контексте подсказки типа это имеет особое значение.