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

Question:

Now that Python 3.10 has been released, is there any preference when indicating that a parameter or returned value might be optional, i.e., can be None. So what is preferred:

Option 1:

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

Option 2:

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

Also, is there any preference between Type | None and None | Type?

Asked By: lmiguelvargasf

||

Answers:

PEP 604 covers these topics in the specification section.

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]

As @jonrsharpe comments, Union and Optional are not deprecated, so the Union and | syntax are acceptable.


Ɓukasz Langa, a Python core developer, replied on a YouTube live related to the Python 3.10 release that Type | None is preferred over Optional[Type] for Python 3.10+.

enter image description here

Answered By: jkr

Non-authoritative, but I would expect Optional when

  • there is a default value provided (probably None)
  • None would be unusual for the caller to pass

While I would expect some Union or | to be used when

  • there is not a default value and/or the default is not None
  • None is also a valid value

See related suggestions at How do I add default parameters to functions when using type hinting?

Answered By: ti7

I would personally go with Option 2 moving forward.

Also, just wanted to add this for awareness, but Python 3.7+ can made to support this syntax using a __future__ import as shown below. This type checks just the same; I actually got the tip from the latest release notes from Pycharm, which I’m currently using.

from __future__ import annotations


def f(parameter: int | None) -> str | None:
    ...
Answered By: rv.kvetch

This is entirely my opinion, but I think that while they are equivalent to the type checker, they express very different intentionality to the human reader. So they are not interchangeable.

If you’re declaring a default value, then type it as def foo(bar: Optional[int] = None) which declares an explicit intent that you don’t have to pass in an argument.

However, if you don’t have a default value, it may be declared def foo(bar: int | None), which declares that this is a required argument where None is one of the valid argument value.

Most of the time, you want to do the former, so most of the time you should still use Optional[int]; but there may be situations where requiring that callers explicitly pass None may be desirable.

A secondary reason is that writing def foo(bar: int | None = None) just looks ugly and confusing with the repeated None.

Answered By: Lie Ryan