Python Type Hints - How to Use typing.cast()2021-07-06
Python’s dynamism means that, although support continues to expand, type hints will never cover every situation. For edge cases we need to use an “escape hatch” to override the type checker.
One such escape hatch is the
# type: ignore comment, which disables a type checking error for a given line.
I previously covered managing such comments, and making them more specific so they do.
Another, preferable escape hatch we can use is casting.
We can cast explicitly with
typing.cast(), or implicitly from
Any with type hints.
With casting we can force the type checker to treat a variable as a given type.
Let’s look at how we can use explicit and implicit casting, and a Mypy feature for managing calls to
When we call
cast(), we pass it two arguments: a type, and a value.
value unchanged, but type checkers will treat the return value as the given type instead of the input type.
For example, we can make Mypy treat an integer as a string:
Checking this program with Mypy, it doesn’t report any errors, but it does debug the types of
y for us:
But, if we remove the
reveal_type() calls and run the code, it crashes:
Usually Mypy would detect this bug, as it knows
int objects do not have an
cast() forced Mypy to treat
y as a
str, so it assumed the call would succeed.
cast() really does do nothing - its special behaviour is only in how type checkers interpret it.
Python’s source code reveals
cast() is a simple no-op function call:
cast() is a normal Python function, calling it does carry a very slight runtime performance penalty.
This will very rarely be an issue.
As usual, you should profile your code before making any assumptions about performance.
The main case to reach for
cast() are when the type hints for a module are either missing, incomplete, or incorrect.
This may be the case for third party packages, or occasionally for things in the standard library.
Take this example:
get_data() has a return type of
dict[str, Any], rather than using stricter per-key types with a
From reading the documentation or source we might find that the
"last_import_time" key always contains a
Therefore, when we access it, we can wrap it in a
cast(), to tell our type checker the real type rather than continuing with
When we encounter missing, incomplete, or incorrect type hints, we can contribute back a fix.
This may be in the package itself, its related stubs package, or separate stubs in Python’s typeshed.
But until such a fix is released, we will need to use
cast() to make our code pass type checking.
Implicit Casting From
It’s worth noting that
Any has special treatment: when we store a variable with type
Any in a variable with a specific type, type checkers treat this as an implicit cast.
We can thus write our previous example without
This kind of implicit casting is the first tool we should reach for when interacting with libraries that return
It also applies when we pass a variable typed
Any as a specifically typed function argument or return value.
cast() directly is often more useful when dealing with incorrect types other than
When we use
cast() to override a third party function’s type, that type be corrected in a later version (perhaps from our own PR!).
After such an update, the
cast() is unnecessary clutter that may confuse readers.
We can detect such unnecessary casts by activating Mypy’s
With this flag turned on, Mypy will log an error for each use of
cast() that casts a variable to the type it already has.
(This provides a similar type-cleanliness check to
warn_unused_ignores, which I covered previously.)
For example, take this unnecessary
Running Mypy with the option active, we see this error:
Activating this option is a great guard for keeping our types clean.
Now don’t go
🎉 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 Enable Postponed Evaluation With __future__.annotations
- Python Type Hints - Duck typing with Protocol
- Python Type Hints - How to Type a Context Manager
Tags: mypy, python
© 2021 All rights reserved.