我试图了解如何使用Optional
类型提示。从PEP 434中,我知道可以将Optional
的{{1}}用作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]
?
答案 0 :(得分:10)
Optional[...]
是Union[..., None]
的简写,它告诉类型检查器特定类型的对象是必需的,或 None
是必需的。 ...
代表任何有效的类型提示,包括复杂的复合类型或更多类型的Union[]
。每当您具有默认值None
的关键字参数时,都应使用Optional
。
因此,在您的两个示例中,您有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
请注意,在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
# 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
旨在标记不存在值。
侧面说明:但是,您要避免在类型提示中使用标准库容器类型,因为您不能说出它们必须包含什么类型;因此,请使用dict
和list
代替typing.List
和typing.Dict
。而且,当只从容器类型中读取时,您也可以接受任何不可变的抽象容器类型。列表和元组是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[List[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
答案 1 :(得分:4)
答案 2 :(得分:2)
虽然接受的答案是正确答案,但要注意的另一件事是,在 kwargs
的上下文中,Optional[...]
和 Union[..., None]
都是多余的和不必要的。如果您立即将 kwarg 设置为 None
,那么 mypy
和 IDE 都假定显而易见,并自动将 arg 视为 Optional[...]
。
集成开发环境:
我的:
对于变量和方法/函数返回值,Optional[...]
仍然是必要的,但是,因为 mypy
在这些情况下无法知道自动假设任何东西。
答案 3 :(得分:0)
直到 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):
....