Self Referential Type Hints with | in Python
I ran into an interesting quirk lately with Python 3.12. Let's take a classic binary tree node class implementation. You would think this would work, but it actually doesn't:
import dataclasses
@dataclasses.dataclass
class Node:
value: float
left: "Node" | None
right: "Node" | None
If I run this, I receive this error:
File "C:\Users\me\blah\test.py", line 20, in Node
left: "Node" | None
~~~~~~~^~~~~~
TypeError: unsupported operand type(s) for |: 'str' and 'NoneType'
This is interesting because since Python 3.10, PEP 604 has been implemented, which means that union types can be written as X | Y
instead of Union[X, Y]
. Also, the python typing docs literally say:
Union type;
Union[X, Y]
is equivalent toX | Y
and means either X or Y.To define a union, use e.g.
Union[int, str]
or the shorthandint | str
. Using that shorthand is recommended.
But we can see that for self-referential types, that's clearly not the case. Neither the PEP, nor the docs mention anything about self-referential types, and their support, so it was interesting to run into this error where type hints with |
don't work with self referential types which need quotes around them.
The Fix
Before Python 3.10, you'd add self-referential type hints in either one of these two ways:
import dataclasses
from typing import Optional, Union
@dataclasses.dataclass
class Node:
value: float
left: Optional["Node"] # <- Option 1
right: Union["Node", None] # <- Option 2
These two methods still work.
An Interesting Aside
To go further down the rabbit hole, if you really really want to use the X | Y
syntax, in Python 3.11 the Self
type was added (docs) and you can use it like this:
import dataclasses
from typing import Self
@dataclasses.dataclass
class Node:
value: float
left: Self | None
right: Self | None