Python Union Types with |
Learn how Python combines types with the | union expression, includes None as an allowed type, simplifies equivalent unions, and uses supported unions in runtime instance and subclass checks.
Intermediate
A union type describes a value that may belong to one of several types. Python writes a union with the
| operator between type objects. This lesson focuses on union expressions themselves and their documented runtime behavior, building on the existing lessons about annotations and type aliases.
Write a union with |
For ordinary built-in type objects,
int | str creates a union meaning “an integer or a string.” Use it in an annotation when either kind of value is acceptable for the function’s contract.
|
|
The same function signature can describe either an integer code or a string code.
Quick check
What does the annotation int | str express?
def label(value: int | str) -> str:
return str(value)
print(label(6))
Include None as one possibility
A return value can use
str | None when a result may be a string or may be absent. This expresses two possible types; it is not the same as saying a function argument is optional because it has a default value.
|
|
None is included as a member of the union. The dictionary lookup supplies either a string or None.
Quick check
Which expression is equivalent to Optional[str]?
def find_title(item_id: int) -> str | None:
return "Guide" if item_id == 1 else None
print(find_title(2))
Recognize equivalent unions
Union expressions discard redundant member types and compare equal regardless of member order. Nested unions are flattened for ordinary type expressions, so adding an already-listed type does not create a different union.
|
|
Equivalent type unions compare equal even when the order differs or a member is repeated.
Quick check
What does int | str | int == str | int evaluate to?
print((int | str | int) == (str | int))
Use a union in runtime checks
Python also accepts a union of runtime-checkable types in
isinstance() and
issubclass(). These checks ask whether an object or class matches at least one union member; they do not perform the job of a static type checker.
|
|
The runtime check succeeds when at least one acceptable union member matches; bool is a subclass of int.
Quick check
Which runtime check asks whether a value matches at least one member of int | str?
print(isinstance("7", int | str))
Know the limits of runtime checks
A string forward reference is another boundary: writing
int | "LaterClass" does not construct a union at runtime because one operand is a string, not a type object. When a forward reference is needed, quote the complete annotation expression, for example
"int | LaterClass".
A type expression can be useful in an annotation even when it is not valid as a runtime
isinstance() check. Parameterized generic aliases such as
list[int] are not valid runtime class-info entries. Do not depend on short-circuit evaluation to hide an invalid member; keep every member valid for the runtime check you are making.
|
|
A parameterized generic such as list[int] cannot be used as a runtime class-info entry in this union check.
Quick check
Why does isinstance([1], str | list[int]) raise TypeError?
try:
isinstance([1], str | list[int])
except TypeError as error:
print(type(error).__name__)
If a union expression becomes long or appears repeatedly, give the expression a meaningful type-alias name using the separate Type Alias Statements lesson. That lesson owns alias syntax; this lesson owns the union expression and its behavior.
Key idea
Use
X | Y to express a union of types. Include
None when absence is a valid possibility, rely on normalization rather than member ordering, and use runtime checks only when every union member is valid for
isinstance() or
issubclass().
Key takeaways
-
A union expression
X | Ysays that a value may match either listed type. -
str | Noneexpresses a string or None; it is equivalent to Optional[str]. - Repeated union members are removed for equivalence, and member order does not change union equality.
-
isinstance()andissubclass()accept unions when their members are valid runtime class-info objects. -
Parameterized generics such as
list[int]are not valid runtime class-info entries.
Thanks for your feedback.
Mark this learning resource complete to track your learning progress.