https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3af1e5ac-553e-49c1-a44f-259608c34f61/1S-nIQHzkHDS5kyZPMCjznw.png

Image by Martin Thoma

Python keeps developing via Python Enhancement Proposals (PEPs). PEP 586 added a feature I love: Literal Types 😍

After reading this article you will know how to use that feature.

If you happen to be new to type annotations, I recommend reading my introduction to type annotations in Python.

Literal Annotation Example

The Literal type needs at least one parameter, e.g. Literal["foo"] . That means that the annotated variable can only take the string "foo" . If you want to accept "bar" as well, you need to writeLiteral["foo", "bar"] .

A complete example:

from typing import Literal
# If you are on Python 3.7 and upgrading is not an option,
#   $ pip install typing_extensions
# to install the backport. Then import it:
#   from typing_extensions import Literaldef parse_bool_str(
    text: str,
    default: Literal["raise", False] = "raise"
) -> bool:
    if text.lower() in ["y", "yes", "true", "1", "on"]:
        return True
    if text.lower() in ["n", "no", "false", "0", "off"]:
        return False
    if default == "raise":
        raise ValueError(f"It's unclear: {text}")
    return False

We could also have annotated default with Union[str, bool] , but that would allow a big range of other values and different behavior.

Please note that you need to run mypy during development (best in your CI pipeline) to find such issues. It will NOT help you during runtime. Read my introduction to type annotations in Python for more about that.

Enum in Python

Instead of using a Literal type, you could use an enum:

def parse_bool_str(
    text: str,
    default: DefaultValues = DefaultBehaviour.RAISE
) -> bool:
    if text.lower() in ["y", "yes", "true", "1", "on"]:
        return True
    if text.lower() in ["n", "no", "false", "0", "off"]:
        return False
    if default == DefaultBehaviour.RAISE:
        raise ValueError(f"It's unclear: {text}")
    return False

You can see that this changes the code in multiple places. For this reason, it might be harder to use Enums for type annotations in an existing codebase.

Pydantic and Literal

When it comes to (de)serialization and data validation, I recently fell in love with pydantic 😍

Let’s test how pydantic deals with Literals:

It works like a charm 🎉

What should I use in new code?