Optional[...]- это сокращенное обозначение Union[..., None], говорящее средству проверки типов, что либо требуется объект определенного типа, либо None требуется. ...обозначает любую допустимую подсказку типа , включая сложные составные типы или Union[]несколько типов. Всякий раз, когда у вас есть аргумент ключевого слова со значением по умолчанию None, вы должны использовать Optional.
Итак, для ваших двух примеров у вас есть типы контейнеров dictи list, но значение по умолчанию для aаргумента ключевого слова показывает, что Noneэто также разрешено, поэтому используйте Optional[...]:
from typing import Optional
def test(a: Optional[dict] = None) -> None:
def test(a: Optional[list] = None) -> None:
Обратите внимание, что технически нет разницы между использованием Optional[]в Union[]или просто добавлением Noneв Union[]. Так Optional[Union[str, int]]и Union[str, int, None]есть в точности то же самое.
Лично я бы всегда придерживался использования Optional[]при установке типа аргумента ключевого слова, который используется = Noneдля установки значения по умолчанию, это Noneлучше документирует причину, почему это разрешено. Более того, это упрощает перемещение Union[...]части в псевдоним отдельного типа или последующее удаление Optional[...]части, если аргумент становится обязательным.
Например, скажем, у вас есть
from typing import Optional, Union
def api_function(optional_argument: Optional[Union[str, int]] = None) -> None:
"""Frob the fooznar.
If optional_argument is given, it must be an id of the fooznar subwidget
to filter on. The id should be a string, or for backwards compatibility,
an integer is also accepted.
"""
то документация улучшается путем вытаскивания Union[str, int]псевдонима типа:
from typing import Optional, Union
SubWidgetId = Union[str, int]
def api_function(optional_argument: Optional[SubWidgetId] = None) -> None:
"""Frob the fooznar.
If optional_argument is given, it must be an id of the fooznar subwidget
to filter on. The id should be a string, or for backwards compatibility,
an integer is also accepted.
"""
Рефакторинг для Union[]преобразования псевдонима стал намного проще, потому что Optional[...]он использовался вместо Union[str, int, None]. В Noneконце концов, значение не является «идентификатором субвиджета», оно не является частью значения, Noneоно предназначено для обозначения отсутствия значения.
Боковое примечание: если ваш код не должен поддерживать только Python 3.9 или новее, вам следует избегать использования стандартных библиотечных типов контейнеров в подсказках типов, поскольку вы не можете ничего сказать о том, какие типы они должны содержать. Таким образом , вместо dictи list, использования typing.Dictи typing.List, соответственно. И при чтении только из контейнерного типа вы можете с тем же успехом принять любой неизменяемый абстрактный тип контейнера; списки и кортежи являются Sequenceобъектами, а dictэто Mappingтип:
from typing import Mapping, Optional, Sequence, Union
def test(a: Optional[Mapping[str, int]] = None) -> None:
"""accepts an optional map with string keys and integer values"""
def test(a: Optional[Sequence[Union[int, str]]] = None) -> None:
"""accepts an optional sequence of integers and strings
# print(a) ==> [1, 2, 3, 4, 'a', 'b']
# or
# print(a) ==> None
В Python 3.9 и выше все стандартные типы контейнеров были обновлены для поддержки использования их в подсказках типов, см. PEP 585 . Но , хотя теперь вы можете использовать dict[str, int]или list[Union[int, str]], вы все равно можете использовать более выразительные Mappingи Sequenceаннотации, чтобы указать, что функция не будет изменять содержимое (они обрабатываются как `` только для чтения '') и что функции будут работать с любой объект, который работает как отображение или последовательность соответственно.
DictиListпечатать, и писать,Optional[Dict]иOptional[List]вместоOptional[dict]...