我应该如何使用Optional类型提示?


84

我试图了解如何使用Optional类型提示。从PEP-484,我知道我可以使用Optionaldef 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[]全部使用?

Answers:


120

Optional[...]是的简写表示法Union[..., None],它告诉类型检查器特定类型的对象是必需的还是 None必需的。...代表任何有效的类型提示,包括复杂的复合类型或Union[]更多类型。只要您有带有默认值的关键字参数None,就应该使用Optional

因此,对于您的两个示例,您有dictlist容器类型,但是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[],或者只是添加NoneUnion[]。所以Optional[Union[str, int]]Union[str, int, None]完全一样。

就个人而言,在设置用于设置默认值的关键字参数的类型时,我始终坚持使用,这说明了允许更好的原因。而且,它使将零件移到单独的类型别名中或在参数变为强制性的情况下稍后删除零件时变得更加容易。Optional[]= NoneNoneUnion[...]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或更高版本,否则您要避免在类型提示中使用标准库容器类型,因为您无法说明它们必须包含哪些类型。因此,分别使用和代替dict和。当仅从容器类型读取时,您也可以接受任何不可变的抽象容器类型。列表和元组对象,同时是一个类型:listtyping.Dicttyping.ListSequencedictMapping

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]],但是您仍然可能希望使用更具表现力的MappingSequence注释来指示函数不会改变内容(它们被视为“只读”),并且该函数可以与分别充当映射或序列的任何对象。


@MartijnPieters我们不需要导入DictList键入和写入Optional[Dict]Optional[List]而不是Optional[dict]...
Alireza

@Alireza是的,我已经在回答中说明了这一点。查找:旁注:但是,您要避免在类型提示中使用标准库容器类型,因为您不能说它们必须包含哪些类型
Martijn Pieters

如果我错了,请更正我,但3.9允许list并被dict用作类型提示(相对于ListDict)。 python.org/dev/peps/pep-0585
user48956

2
@ user48956:我在3.9中添加了一个部分。
马丁·彼得斯

3

直接来自mypy键入模块docs

  • “ Optional [str]只是Union [str,None]的简写或别名。它的存在主要是为了方便功能签名看起来更简洁。”
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.