Literal text refers to a sequence of characters exactly as they appear, enclosed in quotes, and interpreted exactly as written in programming or documentation.
How to use Python typing Literal
Key takeaways:
Python's
Literaltype hint allows specifying exact valid values for variables or function parameters to improve type safety.It works with static type checkers like
mypybut doesn't enforce constraints at runtime.Enums can be combined with
Literalto define valid status values more clearly.
Unionenables combiningLiteralwith other types for greater flexibility.Limitations include no runtime enforcement, verbosity for large sets, and inability to represent dynamic or complex relationships.
Using
Literalenhances code clarity, reduces bugs, and ensures maintainable type hints.
Python, as a dynamically typed language, offers great flexibility but can sometimes lead to runtime errors, such as a null reference or attribute error, that are hard to debug. To counter this, Python introduced type hints, allowing developers to specify the types of variables, function arguments, and return values. One of the powerful features of Python's type hints module is Literal, which enables more precise type checking. In this Answer, we’ll explore how to use Python's Literal with the static type checker mypy with executable examples.
What is Literal?
The Literal is a type hint that allows us to specify that a variable or function parameter can only take on specific values. This is particularly useful for function parameters that should only accept a fixed set of known values.
Setting up mypy
Before diving into Literal, ensure we have mypy installed. We can install it using pip:
pip install mypy
The following command is used to run mypy on code:
mypy file-name.py
Practical examples
Consider an example where we define a function to make HTTP requests. The method parameter should only accept specific HTTP methods.
from typing import Literaldef make_request(method: Literal['GET', 'POST', 'PUT', 'DELETE'], url: str) -> None:print(f"Making a {method} request to {url}")make_request('GET', 'https://example.com') # This will workmake_request('POST', 'https://example.com') # This will workmake_request('PATCH', 'https://example.com') # This will raise an error during type checking
Explanation
Lines 3–4: The
make_requestfunction is designed with two parameters:method, annotated withLiteral['GET', 'POST', 'PUT', 'DELETE'], restricting it to only accept specific HTTP methods (GET,POST,PUT, orDELETE), andurl, a standard string (str) representing the request destination. The function has a return type-> None, indicating it doesn’t return a value but rather prints a formatted message in its single-line implementation within the function body. This message specifies the HTTP method and the corresponding URL for the request.Lines 6–8: The
make_requestfunction accepts specific HTTP methods ('GET','POST','PUT','DELETE') and a URL parameter. Calls with'GET'or'POST'will succeed, but using'PATCH'will raise a type error during type checking (mypy), indicating that'PATCH'is not a valid method according to the specified constraints.
Using Literal with enums
The Literal works well with
from enum import Enumfrom typing import Literalclass Status(Enum):OPEN = 'open'CLOSED = 'closed'def get_status(status: Literal[Status.OPEN, Status.CLOSED]) -> str:return f"The status is {status.value}"print(get_status(Status.OPEN)) # This will workprint(get_status(Status.CLOSED)) # This will workprint(get_status('in_progress')) # This will raise an error during type checking
Explanation
Lines 4–6: These define the
Statusenum with two members,OPENandCLOSED, each representing a string value ('open'and'closed').Lines 8–9: These define the
get_statusfunction, which takes astatusparameter restricted toStatus.OPENorStatus.CLOSEDusingLiteral. The function returns a string indicating the current status.Lines 11–13: These calls to
get_statuswithStatus.OPENandStatus.CLOSEDwork correctly, printing the corresponding status messages. A call with'in_progress'will raise a type error during type checking because it is not a validstatusvalue.
Combining Literal with other types using Union
We can combine Literal with other types using Union.
from typing import Union, Literaldef get_status(status: Union[Literal['open', 'closed'], int]) -> str:if isinstance(status, int):return f"Status code: {status}"return f"The status is {status}"print(get_status('open')) # This will workprint(get_status(404)) # This will workprint(get_status('error')) # This will raise an error during type checking
Explanation
Lines 3–6: The
get_statusfunction is designed to accept astatusparameter, which can be either a string literal ('open'or'closed') or an integer, as indicated by theUnion[Literal['open', 'closed'], int]type hint. The function returns a string. Inside theget_statusfunction, it checks if thestatusparameter is an integer usingisinstance. Ifstatusis an integer, the function returns a formatted string “Status code: {status}”. Ifstatusis a string literal, the function returns "The status is {status}".Lines 6-8: These lines demonstrate calls to the
get_statusfunction:The
get_status('open')works correctly, printing “The status is open” because'open'is a valid literal according to the type hint.The
get_status(404)works correctly, printing “Status code: 404” because404is an integer.The
get_status('error')will raise a type error during static type checking, because'error'is not included in the specified literals ('open'or'closed').
Limitations of typing literals
Some limitations associated with typing literals in Python are:
No runtime enforcement: Type hints, including
Literal, are only checked by static type checkers (e.g.,mypy) and have no effect at runtime. Invalid values passed to aLiteral-annotated parameter will not raise errors unless explicitly handled in the code.Limited expressiveness:
Literalis best suited for simple values like strings, numbers, or booleans. It cannot easily express complex conditions or relationships between values.Cannot represent dynamic sets:
Literalrequires fixed, predefined values. If the valid options are dynamically generated or based on runtime conditions,Literalis not applicable.Verbosity for large sets: When there are many possible values, using
Literalcan make the code verbose and less maintainable.
Conclusion
Using Literal with mypy allows us to define more precise and expressive type hints in our Python code. This can lead to fewer bugs and clearer, more maintainable code. By following the examples in this Answer, we can start leveraging the power of Literal in our own projects.
Frequently asked questions
Haven’t found what you were looking for? Contact Us
What is literal text?
How do you use literal string?
How is string literal represented in Python?
Free Resources