Mode strings like 'r' or 'json' are bug magnets; Literal catches typos at check time.
Literal["r", "w", "a"] is a type whose only values are those exact constants. Passing "rw" is a checker error, and editors autocomplete the options. Literals can be strings, ints, bools, bytes, enum members or None.
Mode = Literal["fast", "safe"]
def run(mode: Mode = "safe") -> None: ...Narrowing works on literals: after if mode == "fast":, the checker knows mode is Literal["safe"] in the else branch. Plain str variables aren't accepted where a Literal is expected unless the checker can prove the value, so use Final or literal-typed variables for constants you pass around.
Final marks a name that must not be reassigned (MAX_RETRIES: Final = 3); the checker infers the literal type Literal[3]. On class attributes it also forbids overriding in subclasses. The @final decorator forbids subclassing a class or overriding a method.
Enums are often the better choice when values have behaviour, are used across many modules, or need iteration (for c in Color). Literals shine for small option sets at API boundaries, where users pass plain strings. Both support exhaustiveness checking, covered in the narrowing lesson.
Example
from typing import Final, Literal, get_args
Format = Literal["json", "csv"]
DEFAULT_FORMAT: Final = "json" # checker: Literal['json'], no reassignment
def export(rows: list[dict[str, int]], fmt: Format = DEFAULT_FORMAT) -> str:
if fmt == "json":
return "json:" + str(rows)
return "csv:" + ",".join(str(r) for r in rows) # fmt: Literal['csv']
print(export([{"a": 1}])) # json:[{'a': 1}]
print(export([{"a": 1}], "csv")) # csv:{'a': 1}
print(get_args(Format)) # ('json', 'csv')
user_choice = "xml"
if user_choice in get_args(Format): # validate untrusted strings at runtime
print("ok")
else:
print("unsupported:", user_choice) # unsupported: xml
# export([], "xml") -> checker error: incompatible type "Literal['xml']"
# DEFAULT_FORMAT = "csv" -> checker error: cannot assign to final nameRemember
- Literal[...] limits a value to exact constants
- Final forbids reassignment and keeps the literal type
- get_args() turns a Literal into a tuple for runtime validation
Watch out for
- Assuming Literal validates at runtime; untrusted input still needs an explicit check.