You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/posts/2025/2025-02-01-python-type-hints.md
+97-1Lines changed: 97 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,7 @@ categories:
7
7
comments: true
8
8
date:
9
9
created: 2025-02-01
10
-
updated: 2025-08-17
10
+
updated: 2025-08-20
11
11
---
12
12
13
13
# Python Type Hints
@@ -18,6 +18,8 @@ Type hints ([PEP 484](https://peps.python.org/pep-0484/)) have been a major focu
18
18
19
19
Today, type hints are essential for modern Python development. They significantly enhance IDE capabilities and AI-powered development tools by providing better code completion, static analysis, and error detection. This mirrors the evolution we've seen with TypeScript's adoption over traditional JavaScript—explicit typing leads to more reliable and maintainable code.
20
20
21
+
The majority of this post is based on [MyPy documentation](https://mypy.readthedocs.io/).
22
+
21
23
!!! note "Typed Python vs data science projects"
22
24
We know that type hints are [not very popular among data science projects](https://engineering.fb.com/2024/12/09/developer-tools/typed-python-2024-survey-meta/) for [some reasons](https://typing.python.org/en/latest/guides/typing_anti_pitch.html), but we won't discuss them here.
23
25
@@ -613,6 +615,100 @@ class MyList[T](Sequence[T]):
613
615
raiseTypeError(...)
614
616
```
615
617
618
+
## Literal and Final
619
+
620
+
`Literal` types may contain one or more literal `bool`, `int`, `str`, `bytes`, and `enum` values. Which means `Literal[3.14]` is not a valid literal type.
621
+
622
+
If you find repeating the value of the variable in the type hint to be tedious, you can instead change the variable to be `Final` (see Final names, methods and classes):
623
+
624
+
```python hl_lines="5 7"
625
+
from typing import Final, Literal
626
+
627
+
defexpects_literal(x: Literal[19]) -> None: pass
628
+
629
+
c: Final =19
630
+
631
+
reveal_type(c) # Revealed type is "Literal[19]?"
632
+
expects_literal(c) # ...and this type checks!
633
+
```
634
+
635
+
Literals containing two or more values are equivalent to the union of those values. So, `Literal[-3, b"foo", MyEnum.A]` is equivalent to `Union[Literal[-3], Literal[b"foo"], Literal[MyEnum.A]]`. So we can has below code:
docs/posts/2025/scripts/mypy_literal.py:11: error: Argument 1 to "paint" has incompatible type"Literal['turquoise']"; expected "Literal['red', 'blue', 'yellow', 'purple', 'green', 'orange']" [arg-type]
654
+
Found 1 error in 1 file (checked 1 source file)
655
+
```
656
+
657
+
## Discriminated union types
658
+
659
+
We can use [Literals](#literal-and-final) to create [discriminated union types](https://mypy.readthedocs.io/en/stable/literal_types.html#tagged-unions) for [type narrowing](https://mypy.readthedocs.io/en/stable/type_narrowing.html).
# Doing `if isinstance(w, Wrapper[int])` does not work: isinstance requires
698
+
# that the second argument always be an *erased* type, with no generics.
699
+
# This is because generics are a typing-only concept and do not exist at
700
+
# runtime in a way `isinstance` can always check.
701
+
#
702
+
# However, we can side-step this by checking the type of `w.inner` to
703
+
# narrow `w` itself:
704
+
ifisinstance(w.inner, int):
705
+
reveal_type(w) # Revealed type is "Wrapper[int]"
706
+
else:
707
+
reveal_type(w) # Revealed type is "Wrapper[str]"
708
+
```
709
+
710
+
And check [this Pydantic doc](https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions-with-str-discriminators) to see how Pydantic use `Field(discriminator='...')` to handle discriminators.
0 commit comments