That title is a handful, so let me share a code snippet first...
class Parent:
class GenericNode():
def __init__(self, name: str) -> None:
self.name: str = name
class SpecificNode(Node):
def __init__(self, name: str) -> None:
super().__init__(name)
self.node_list: list[Parent.GenericNode] = []
def add_node(self, node: Parent.GenericNode) -> None:
self.node_list.append(node)
Here, in the add_node(self, node: Parent.GenericNode)
method, the Parent.GenericNode
type is not recognised by the Python interpreter - it throws a NameError
when trying to run it. That seems very odd to me, since the exact same type was just used to define the type of the list in the constructor.
However, when I take everything one level up - and get rid of the Parent
class altogether like this:
class Node():
def __init__(self, name: str) -> None:
self.name: str = name
class SpecificNode(Node):
def __init__(self, name: str) -> None:
super().__init__(name)
self.node_list: list[Node] = []
def add_node(self, node: Node) -> None:
self.node_list.append(node)
... everything miraculously works, and the code executes.
I came across this answer, which suggests using a string in place of a type hint like so: add_node(self, node: 'Parent.GenericNode')
. Now this code executes and even mypy
manages to sucessfully type-check it.
However, as that answer also mentions, this (or rather the equivalent of using from __future__ import annotations
) should have been the default since Python 3.10 and I'm testing this on Python 3.11. Was it perhaps not made part of the release?
I tried searching online but couldn't find any mention of this particular behaviour. Could someone please help me shed some light on this?
EDIT: I should also mention, I find it weird that there would be a problem with the Parent
being undefined like many other answers suggest (e.g.), because that very type annotation is used in inside another method on the same level. The oddity to me is that in parameter type hint it does not work, but in a member variable type hint, it does.