Python 3.10+: Optional[Type] 或 Type | None

59

现在Python 3.10已发布,当指示参数或返回值可能是可选的,即可以为None时,是否有任何偏好。那么哪个更受欢迎:

选项1:

def f(parameter: Optional[int]) -> Optional[str]:

选项2:

def f(parameter: int | None) -> str | None:

另外,Type | NoneNone | Type 之间有没有任何偏好?


6
3.10 版本的文档并没有弃用 Union 或 Optional 去使用新的语法,所以由你自己决定。 - jonrsharpe
5
如果不确定,就选择更短的。 - F.D.Castel
3
Optional是为了表示“类型或None”而发明的。它的内在含义就是“类型或None”,所以根据良好的基本原则,使用type | None更加明确。它还可以摆脱一个标识符。只要你确定你的Python版本支持它,这些都是选择新语法的客观理由。 - NeilG
4个回答

69

PEP 604 在规范部分涵盖了这些主题。

The existing typing.Union and | syntax should be equivalent.

int | str == typing.Union[int, str]

The order of the items in the Union should not matter for equality.

(int | str) == (str | int)
(int | str | float) == typing.Union[str, float, int]

Optional values should be equivalent to the new union syntax

None | t == typing.Optional[t]

正如@jonrsharpe的评论所述,UnionOptional并未被废弃,因此Union|语法是可以接受的。


Łukasz Langa, 一位Python核心开发者,在与Python 3.10发布相关的YouTube直播中回复说,对于Python 3.10+,Type | NoneOptional[Type]更受欢迎。

enter image description here


3
如果@Imiguelvargasf这么说,他会说他写了PEP(《Python增强提案》);就我来看,这只是个人偏好问题,尽管我个人无法想象在哪种情况下我能发现旧语法更清晰或更易读。另一方面,它更具向后兼容性! - Alex Waygood
2
@AlexWaygood - 我在 PEP 上没有看到 Lukasz Langa 的名字,你确定是他写的吗? - jkr
@jakub 对不起!看来我把PEP 585和PEP 604搞混了。感谢您的纠正。太多的类型PEP,时间太少,等等。 - Alex Waygood
6
如果我们需要在YouTube评论中寻找好的做法,那真是一种非常令人沮丧的情况。 - undefined

24

在未来的发展中,我个人会选择第二个选项

另外,只是想提醒一下,Python 3.7+可以使用__future__导入来支持此语法,如下所示。这种类型检查与以前相同;实际上,我是从我目前使用的Pycharm的最新发布说明中得到的提示。

from __future__ import annotations


def f(parameter: int | None) -> str | None:
    ...

8
这完全是我的个人意见,但我认为虽然它们等同于类型检查器,但它们表达给人的意图却非常不同。因此它们不能互换使用。
如果您声明了一个默认值,则将其类型设置为def foo(bar: Optional[int] = None),这声明了一个明确的意图,即您不必传入参数。
但是,如果您没有默认值,则可以将其声明为def foo(bar: int | None),这表示这是一个必需的参数,其中None是有效参数值之一。
大多数情况下,您希望执行前者,因此大多数时候仍应使用Optional[int];但是在某些情况下,要求调用者显式传递None可能是可取的。
第二个原因是写def foo(bar: int | None = None)看起来很丑,重复的None也容易混淆。

11
请注意,Optional与参数是否可选无关。而参数 bar = 12 的意思是该参数 bar 是可选的,但绝不是 Optional - MisterMiyagi
有趣的风格指南,表达在第二和第三段。然而,在第一段完全适用之前,这两个应该被程序员接受;) - Joël
我完全同意。使用 some_type | None 使代码难以理解,因为管道操作符 | 可以有多种用法。将一个类型和 None 做按位或运算的含义是什么?这非常令人困惑... - Péter Szilvási
2
管道操作符 | 是一个非常常见的表示“或”的符号,@PéterSzilvási,在许多系统中一直延续到上个世纪。它读起来像黄油一样顺畅。 - NeilG

4
非正式的说法,但我期望在以下情况下使用Optional
  • 提供了默认值(可能是None
  • 对于调用方来说传递None是不寻常的
我期望在以下情况下使用Union|
  • 没有默认值和/或默认值不是None
  • None也是一个有效的值
请查看如何在使用类型提示时为函数添加默认参数?中相关的建议。

1
对于默认值为None的参数,我真的认为Optional[T]太啰嗦了,与其他有某种形式的T?的语言相比。由于类型提示是一个装饰性质的东西,有时候我只会写成foo(comment: str = None)这样的形式。而且,像__init__()返回None这样的函数不应该有返回值注释,-> None看起来让我感觉很糟糕。但这并不是“标准”,使用代码检查工具时可能会有警告。 - Tomasz Gandor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接