391

Suppose I have a function:

def get_some_date(some_argument: int=None) -> %datetime_or_None%:
    if some_argument is not None and some_argument == 1:
        return datetime.utcnow()
    else:
        return None

How do I specify the return type for something that can be None?

Alex Waygood
  • 6,304
  • 3
  • 24
  • 46
exfizik
  • 5,351
  • 4
  • 23
  • 26
  • 7
    bad naming applied in `typing` package. It could be `Nullable[X]` as an equivalent for `Union[None, X]`. Thus no need to explain that `Optional` is not for optional argument https://docs.python.org/3/library/typing.html#typing.Optional – Sławomir Lenart Mar 11 '21 at 13:31
  • 3
    That naming is so bad. It should be called Nullable[x]. It leads to using nonsensical terms in libraries docs such as "Required Optional fields" in pydantic (instead of just "Required Nullable fields") – PawelRoman Oct 15 '22 at 16:42

2 Answers2

612

You're looking for Optional.

Since your return type can either be datetime (as returned from datetime.utcnow()) or None you should use Optional[datetime]:

from typing import Optional

def get_some_date(some_argument: int=None) -> Optional[datetime]:
    # as defined

From the documentation on typing, Optional is shorthand for:

Optional[X] is equivalent to Union[X, None].

where Union[X, Y] means a value of type X or Y.


If you want to be explicit due to concerns that others might stumble on Optional and not realize it's meaning, you could always use Union:

from typing import Union

def get_some_date(some_argument: int=None) -> Union[datetime, None]:

But I doubt this is a good idea, Optional is an indicative name and it does save a couple of keystrokes.

As pointed out in the comments by @Michael0x2a Union[T, None] is tranformed to Union[T, type(None)] so no need to use type here.

Visually these might differ but programatically, in both cases, the result is exactly the same; Union[datetime.datetime, NoneType] will be the type stored in get_some_date.__annotations__*:

>>> from typing import get_type_hints
>>> print(get_type_hints(get_some_date))
{'return': typing.Union[datetime.datetime, NoneType],
 'some_argument': typing.Union[int, NoneType]}

*Use typing.get_type_hints to grab the objects' __annotations__ attribute instead of accessing it directly.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • 18
    You can simplify `Union[datetime, type(None)]` to `Union[datetime, None]` -- according to [PEP 484](https://www.python.org/dev/peps/pep-0484/#using-none), using `None` within a type annotation is always treated as being equivalent to `type(None)`. (The `typing` documentation actually uses `None` in most cases, but doesn't here, which is an oversight). – Michael0x2a Sep 10 '16 at 23:59
  • @Michael0x2a didn't know that, interesting. Added it :) – Dimitris Fasarakis Hilliard Sep 11 '16 at 00:22
  • 5
    I went ahead and submitted [a patch](http://bugs.python.org/issue28073) to fix this just now, so hopefully the docs will be more consistent about this in the near future! – Michael0x2a Sep 11 '16 at 00:31
  • 4
    The `Optional[T]` type is well-known in the functional programming community. The reader will not only know that it means `Union[T, None]`, but will also recognise the use pattern that the function shall return None when there is no meaningful answer, there is an error, or the result is not found. – wks Apr 22 '19 at 08:01
  • 26
    In Python 3.10 `datetime | None` can be used. – Mario Ishac Feb 20 '21 at 04:27
  • @MarioIshac indeed. For those interested, see [PEP 604](https://www.python.org/dev/peps/pep-0604/). – Dimitris Fasarakis Hilliard Feb 24 '21 at 11:54
  • I lose track of which latest change goes into effect in what new Python version – axolotl Apr 14 '22 at 23:43
45

You could just use the vertical line datetime | None (similar to OR operator):

def get_some_date() -> datetime | None:
   # rest of code
elano7
  • 1,584
  • 1
  • 18
  • 18