196

我试图了解如何使用Optional类型提示。从PEP-484,我知道我可以Optional使用def test(a: int = None)asdef 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[]

4

4 回答 4

299

Optional[...]是 的简写符号Union[..., None],告诉类型检查器需要 None需要特定类型的对象。...代表任何有效的类型提示,包括复杂的复合类型或Union[]更多类型。每当您有一个具有默认值的关键字参数时None,您都应该使用Optional. (注意:如果您的目标是 Python 3.10 或更新版本,PEP 604引入了更好的语法,见下文)。

因此,对于您的两个示例,您有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[]在 a 上使用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毕竟,该值不是“子小部件 id”,它不是值的一部分,None旨在标记缺少值。

旁注:除非您的代码只需要支持 Python 3.9 或更高版本,否则您要避免在类型提示中使用标准库容器类型,因为您无法说明它们必须包含哪些类型。因此,代替dictand ,分别list使用typing.Dictand 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"""
    # 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]or list[Union[int, str]],但您仍然可能希望使用更具表现力MappingSequence注释来指示函数不会改变内容(它们被视为“只读”),并且函数可以使用分别用作映射或序列的任何对象。

Python 3.10 将|联合运算符引入类型提示,请参阅PEP 604。而不是Union[str, int]你可以写str | int. 与其他类型提示语言一致,在 Python 3.10 及更高版本中表示可选参数的首选(且更简洁)方式现在是Type | None,例如str | Noneor list | None

于 2018-08-06T14:39:44.810 回答
20

Directly from mypy typing module docs.

  • “Optional[str] is just a shorthand or alias for Union[str, None]. It exists mostly as a convenience to help function signatures look a little cleaner.”</li>
于 2020-08-16T22:00:09.837 回答
11

虽然接受的答案是正确答案,但需要注意的另一件事是,在 的上下文中kwargs,两者Optional[...]Union[..., None]都是多余且不必要的。如果您立即将您的 kwarg 设置为None,则mypyIDE 和 IDE 都假定显而易见并自动将 arg 视为Optional[...].

IDE:

在此处输入图像描述

我的:

mypy 自动 可选

但是,对于变量和方法/函数的返回值,Optional[...]仍然有必要,因为mypy在这些情况下无法知道自动假设任何内容。

于 2021-04-02T16:21:44.743 回答
-1

在 Python 3.9 之前,如果您想提示可空值,您有两种选择:

import typing

def foo(bar: typing.Optional[str]):
    ....

def foo(bar: typing.Union[str, None]):
    ....

从 Python 3.9 开始,您不需要使用输入模块:

def foo(bar: str = None):
    ....
于 2021-05-03T14:02:08.007 回答