Say I have a base class
from typing import List, Optional
class Node:
def __init__(self, name: str) -> None:
self.name = name
self.children: List['Node'] = []
...
and a subclass
class PropertiesNode(Node):
def __init__(self, name: str, properties: List[str], inherit: Optional['PropertiesNode']) -> None:
Node.__init__(self, name)
self.properties = set(properties)
if inherit:
self.properties.update(inherit.properties)
self.children = deepcopy(inherit.children)
for child in self.children:
child.properties.update(properties) # ERR: "Node" has no attribute "properties" [attr-defined]
As you can see, mypy (rightly) flags an error there, as Node.children was explicitly given a type of List[Node].
So I read up on generic types, and it seems to me the solution is to use TypeVars and Generic:
from typing import Generic, List, Optional, TypeVar
N = TypeVar('N', bound='Node')
P = TypeVar('P', bound='PropertiesNode')
class Node(Generic[N]):
def __init__(self: N, name: str) -> None:
self.name = name
self.children: List[N] = []
class PropertiesNode(Node[P]):
def __init__(self: P, name: str, properties: List[str], inherit: Optional[P]) -> None:
Node.__init__(self, name)
self.properties = set(properties)
if inherit:
self.properties.update(inherit.properties)
self.children = deepcopy(inherit.children)
for child in self.children:
child.properties.update(properties)
However, now when I instantiate the classes, I get
foo = Node("foo") # ERR Need type annotation for "foo" [var-annotated]
bar = PropertiesNode("bar", ["big", "green"], None) # ERR Need type annotation for "bar" [var-annotated]
Now, I could silence these by doing
foo: Node = Node("foo")
bar: PropertiesNode = PropertiesNode(...)
but why does that silence it - I'm not giving mypy any new info there? The more I think about, the less Generic seems like the right choice, because the thing is: all instances of Node or PropertiesNode will have self.children that are of exactly the same type as self.
But if I remove the Generic[N] from class Node(Generic[N]):, I end up with the original error again:
class PropertiesNode(Node):
...
child.properties.update(properties) # ERR "N" has no attribute "properties" [attr-defined]