Explain Codes LogoExplain Codes Logo

How to specify "nullable" return type with type hints

python
type-hints
nullable-types
dataclasses
Nikita BarsukovbyNikita Barsukov·Nov 2, 2024
TLDR

In Python, denote a nullable return type with the Optional type hint for versions before 3.10, or utilize the | operator to combine the type with None for version 3.10+.

Version <3.10 example:

from typing import Optional def func() -> Optional[int]: return None # or 42, if life, universe and everything

In version 3.10+ :

def func() -> int | None: return None # or a 'random' prime number; return 2

##sarcasm


## Type hints: Where and why?

Using nullable types **communicates** to the function user a kind of **expectation management**. Incorporating these in specific scenarios have big advantages:

- In **data retrieval** from a database where you sometimes get ghost data.
- When writing **parsing** functions and the text looks like gibberish.
- **Factory pattern** functions that return a product or `None` if it breaks down (who said warranty).

**Good Practice**: Documentation is the key to reduce future headaches. Always document when and why your function might return `None`.

## Access type hints your way

To expose the underbelly of **type annotations**, Python offers `typing.get_type_hints`. It's helpful when you're knee-deep in **metaprogramming** and **dynamic type checking**.

Here's how to use it:
```python
from typing import get_type_hints

def func() -> int | None:
    return None  # *insert your favourite number here*

print(get_type_hints(func))
# Output: {'return': Union[int, NoneType]}

Quick tip: Call get_type_hints when you need dynamic function behaviour introspection. It's almost like having a debugger on a leash.

Beware of the null

  • Null return type hints left unchecked and undocumented is a ghost waiting to haunt you.
  • Be cautious while on the decorator platform. Invalid type hints might just jump out when the train is about to leave.

Solution: Periodically compel your type checker to walk through your haunted codebase. This will ensure your return type hints are not just for the show.

Union vs Optional: The duel

Python < 3.10

Optional[ReturnType] is more readable than the less favoured Union[ReturnType, None].

Python ≥ 3.10

The | operator offers a concise alternative to Optional.

ReturnType | None vs Optional[ReturnType]

Remember: Optional is the hyped name of Union[ReturnType, None]. Choosing between them is similar to the tabs vs spaces debate.

Working with data classes

Optional or the | operator also plays nice in data classes:

from dataclasses import dataclass from typing import Optional @dataclass class Example: attribute: Optional[int] = None # Can be None because it prefers to be undefined.

In Python 3.10+:

from dataclasses import dataclass @dataclass class Example: attribute: int | None = None # Because sometimes, math needs a break too.

Hint: Consistency is key for maintainability and readability.