我应该如何使用 Optional 类型提示?
How should I use the Optional type hint?
我正在尝试了解如何使用 Optional
类型提示。从 PEP-484,我知道我可以将 Optional
用于 def test(a: int = None)
作为 def test(a: Union[int, None])
或 def test(a: Optional[int])
。
但是下面的例子怎么样?
def test(a : dict = None):
#print(a) ==> {'a': 1234}
#or
#print(a) ==> None
def test(a : list = None):
#print(a) ==> [1,2,3,4, 'a', 'b']
#or
#print(a) ==> None
如果 Optional[type]
似乎与 Union[type, None]
意思相同,我为什么要使用 Optional[]
?
Optional[...]
是 Union[..., None]
的 shorthand 符号,告诉类型检查器需要特定类型的对象, 或 None
是必需的。 ...
代表 任何有效类型提示 ,包括复杂的复合类型或更多类型的 Union[]
。每当你有一个默认值为 None
的关键字参数时,你应该使用 Optional
。 (注意:如果您的目标是 Python 3.10 或更高版本,PEP 604 引入了更好的语法,请参见下文)。
因此对于您的两个示例,您有 dict
和 list
容器类型,但是 a
关键字参数的默认值表明 None
也是允许的所以使用 Optional[...]
:
from typing import Optional
def test(a: Optional[dict] = None) -> None:
#print(a) ==> {'a': 1234}
#or
#print(a) ==> None
def test(a: Optional[list] = None) -> None:
#print(a) ==> [1, 2, 3, 4, 'a', 'b']
#or
#print(a) ==> None
在 Union[]
上使用 Optional[]
或仅将 None
添加到 Union[]
在技术上没有区别。所以 Optional[Union[str, int]]
和 Union[str, int, None]
完全一样。
就我个人而言,在为使用 = None
设置默认值的关键字参数设置类型时,我会坚持 总是 使用 Optional[]
,这记录了为什么 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
# subwidget ids used to be integers, now they are strings. Support both.
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
值毕竟不是 'subwidget id',它不是值的一部分,None
是为了标记值的缺失。
旁注:除非您的代码只需要支持 Python 3.9 或更新版本,否则您希望避免在类型提示中使用标准库容器类型,因为您无法说明它们必须支持哪些类型包含。因此,请分别使用 typing.Dict
和 typing.List
,而不是 dict
和 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"""
# print(a) ==> {'a': 1234}
# or
# print(a) ==> None
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
注释表明函数不会改变内容(它们被视为 'read only'),并且函数可以与 any[=90 一起使用=] 分别用作映射或序列的对象。
Python 3.10 将 |
联合运算符引入到类型提示中,请参阅 PEP 604。你可以写 str | int
而不是 Union[str, int]
。与其他类型提示语言一致,在 Python 3.10 及更高版本中表示可选参数的首选(且更简洁)方式现在是 Type | None
,例如str | None
或 list | None
.
直接来自 mypy typing module docs.
- “Optional[str] 只是一个 shorthand 或 Union[str, None] 的别名。它的存在主要是为了方便函数签名看起来更简洁。”
虽然接受的答案是正确答案,但需要注意的另一件事是,在 kwargs
的上下文中,Optional[...]
和 Union[..., None]
是多余的和不必要的。如果你立即将你的 kwarg 设置为 None
,那么 mypy
和 IDE 都会假设明显的并且 自动 将 arg 视为 Optional[...]
.
IDE:
mypy:
对于变量和 method/function return 值,Optional[...]
仍然是必要的,但是,因为 mypy
不知道,在那些情况下,自动假设任何东西。
直到 Python 3.9 如果你想提示一个 nullable 值你有两个选择:
import typing
def foo(bar: typing.Optional[str]):
....
def foo(bar: typing.Union[str, None]):
....
从 Python 3.9 开始,您不需要使用输入模块:
def foo(bar: str = None):
....
请注意,自 Python 3.10 起,您可以简化代码并键入:
def foo(
bar: int | None = None,
another_bar: Callable[[int, list, float, datetime | None], str],
):
我正在尝试了解如何使用 Optional
类型提示。从 PEP-484,我知道我可以将 Optional
用于 def test(a: int = None)
作为 def test(a: Union[int, None])
或 def test(a: Optional[int])
。
但是下面的例子怎么样?
def test(a : dict = None):
#print(a) ==> {'a': 1234}
#or
#print(a) ==> None
def test(a : list = None):
#print(a) ==> [1,2,3,4, 'a', 'b']
#or
#print(a) ==> None
如果 Optional[type]
似乎与 Union[type, None]
意思相同,我为什么要使用 Optional[]
?
Optional[...]
是 Union[..., None]
的 shorthand 符号,告诉类型检查器需要特定类型的对象, 或 None
是必需的。 ...
代表 任何有效类型提示 ,包括复杂的复合类型或更多类型的 Union[]
。每当你有一个默认值为 None
的关键字参数时,你应该使用 Optional
。 (注意:如果您的目标是 Python 3.10 或更高版本,PEP 604 引入了更好的语法,请参见下文)。
因此对于您的两个示例,您有 dict
和 list
容器类型,但是 a
关键字参数的默认值表明 None
也是允许的所以使用 Optional[...]
:
from typing import Optional
def test(a: Optional[dict] = None) -> None:
#print(a) ==> {'a': 1234}
#or
#print(a) ==> None
def test(a: Optional[list] = None) -> None:
#print(a) ==> [1, 2, 3, 4, 'a', 'b']
#or
#print(a) ==> None
在 Union[]
上使用 Optional[]
或仅将 None
添加到 Union[]
在技术上没有区别。所以 Optional[Union[str, int]]
和 Union[str, int, None]
完全一样。
就我个人而言,在为使用 = None
设置默认值的关键字参数设置类型时,我会坚持 总是 使用 Optional[]
,这记录了为什么 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
# subwidget ids used to be integers, now they are strings. Support both.
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
值毕竟不是 'subwidget id',它不是值的一部分,None
是为了标记值的缺失。
旁注:除非您的代码只需要支持 Python 3.9 或更新版本,否则您希望避免在类型提示中使用标准库容器类型,因为您无法说明它们必须支持哪些类型包含。因此,请分别使用 typing.Dict
和 typing.List
,而不是 dict
和 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"""
# print(a) ==> {'a': 1234}
# or
# print(a) ==> None
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
注释表明函数不会改变内容(它们被视为 'read only'),并且函数可以与 any[=90 一起使用=] 分别用作映射或序列的对象。
Python 3.10 将 |
联合运算符引入到类型提示中,请参阅 PEP 604。你可以写 str | int
而不是 Union[str, int]
。与其他类型提示语言一致,在 Python 3.10 及更高版本中表示可选参数的首选(且更简洁)方式现在是 Type | None
,例如str | None
或 list | None
.
直接来自 mypy typing module docs.
- “Optional[str] 只是一个 shorthand 或 Union[str, None] 的别名。它的存在主要是为了方便函数签名看起来更简洁。”
虽然接受的答案是正确答案,但需要注意的另一件事是,在 kwargs
的上下文中,Optional[...]
和 Union[..., None]
是多余的和不必要的。如果你立即将你的 kwarg 设置为 None
,那么 mypy
和 IDE 都会假设明显的并且 自动 将 arg 视为 Optional[...]
.
IDE:
mypy:
对于变量和 method/function return 值,Optional[...]
仍然是必要的,但是,因为 mypy
不知道,在那些情况下,自动假设任何东西。
直到 Python 3.9 如果你想提示一个 nullable 值你有两个选择:
import typing
def foo(bar: typing.Optional[str]):
....
def foo(bar: typing.Union[str, None]):
....
从 Python 3.9 开始,您不需要使用输入模块:
def foo(bar: str = None):
....
请注意,自 Python 3.10 起,您可以简化代码并键入:
def foo(
bar: int | None = None,
another_bar: Callable[[int, list, float, datetime | None], str],
):