Python Type Hints - How to Use typing.Literal2021-07-09
To put it tautologically, type hints normally specify the types of variables.
But when a variable can only contain a limited set of literal values, we can use
typing.Literal for its type.
This allows the type checker to make extra inferences, giving our code an increased level of safety.
In this post we’ll look at how to use
Literal and the benefits it provides.
Literal, we simply parametrize it with the allowed literal values:
We’ve declared that the
game variable has only two possible values: the strings
typing.Literal was defined in PEP 586, which defined the allowed types for values:
None- a special case for convenience:
Literal[None]is equivalent to
Additionally, we can nest
Literals, which combines their values.
Literal[Literal[1, 2], Literal, 4] is equivalent to
Literal[1, 2, 3, 4].
When we use a
Literal type, the type checker can ensure that:
- Assignments use permitted values.
- Function calls use permitted values.
- Comparisons use permitted values.
- Conditional blocks use the subset of values they compared against, via specialized type narrowing.
if/elifstatements use all permitted values, when we use exhaustiveness checking.
Let’s examine those in turn.
Imagine we make a typo and assign an incorrect value to our
Mypy will spot this for us:
2. Function calls
Similarly, say we pass an unsupported value to a function using
Mypy will also find this:
We might also use an unsupported value in a comparison against a
In this case, as long as we have Mypy’s
strict_equality option active, it will find the error:
Note that, at time of writing, Mypy only performs strict equality checks for
A more complicated comparison, like
game in ["owela"], will not fail the strict equality check.
4. Conditional block type narrowing
When we make a comparison against a variable’s type, Mypy can perform type narrowing to infer the variable has a restricted type within the conditional block.
We previously covered how to do this with constructs like
When we compare a
Literal against one or more values, Mypy will also perform type narrowing.
It can infer the value has a more limited
Literal type within the conditional block.
For example, take this code:
When we run Mypy on it, the
reveal_type() debug calls show us the narrowed
5. Exhaustiveness Checking
Exhaustiveness checking is when the type checker ensures we cover all possible options for a variable’s type or value.
Python type hints have no formal specification for exhaustiveness checking yet, but we can emulate it with the
If we use a function that accepts
NoReturn as a value type, any call to it will fail, since
NoReturn matches no type.
This technique was documented for
Enum types in a blog post by Haki Benita.
We can also use it with
Imagine we forgot to handle the
"chess" case in our
The error message is not particularly clear, as we’re only emulating exhaustiveness checking.
But it does report the unhandled value is
Literal['chess'], and the approximate line to correct that on.
Exhaustiveness checking is particularly useful when we introduce a new value into our system.
We can add the value to our first shared
Literal definition, and then use Mypy to find places that need updating.
I hope that you’ve literally enjoyed this post,
🎉 My book Speed Up Your Django Tests is now up to date for Django 3.2. 🎉
Buy now on Gumroad
One summary email a week, no spam, I pinky promise.
- Python Type Hints - How to Use @overload
- Python Type Hints - How to Use typing.cast()
- Python Type Hints - How to Type a Context Manager
Tags: mypy, python
© 2021 All rights reserved.