Согласно части 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. Только в контексте подсказки типа это имеет особое значение.