Working with Python 3.6 under Win10x64 in PyCharm 2017.
I know typing (or even type hinting) is kind of unpopular in the Python community, but please bear with me.
Let's say I have a Node
class to create tree-like containers with arbitrary contents. Here is a highly simplified version:
from typing import Any, List
class Node:
def __init__(self, content: Any, parent: 'Node' = None):
self.content = content # type: Any
self.parent = parent # type: 'Node'
self.children = [] # type: List['Node']
if parent:
parent.children.append(self)
@property
def descendants(self) -> List['Node']:
"""Returns a list of nodes that are descendant from the node starting with its first child, then its
descendants (and ending with the last descendant of its last child)."""
out = []
for ch in self.children:
out.append(ch)
for desc in ch.descendants:
out.append(desc)
return out
I am trying to use proper type hints. Seeing as I need to indicate the type of parent
to be Node
, I used the quotes as prescribed in PEP 484 for forward references. Same with self.children
, using List['Node']
, and also with the property descendants
.
If I inherit from Node (like so class SpecialNode(Node): pass
), I would expect PyCharm's type checker to be able to infer all the relevant types correctly, such that SpecialNode
is expected in place of Node
.
Consider this:
n_root = Node('foo')
n_0 = Node(10, parent=n_root)
n_01 = Node('bar', parent=n_0)
n_root_and_children = [n_root] + n_root.children
n_root_and_descendants = [n_root] + n_root.descendants
s_root = SpecialNode('special_foo')
s_0 = Node(-10, parent=s_root)
s_01 = Node('special_bar', parent=s_0)
s_root_and_children = [s_root] + s_root.children
s_root_and_descendants = [s_root] + s_root.descendants
Obviously all of this still "works" at runtime, but PyCharm underlines s_root.descendants
in the last line and tells me:
Expected type 'List[SpecialNode]' (matched generic type 'List[TypeVar('_T')]'), got 'List[Node]' instead
What am I doing wrong?
.
EDIT:
Inspired by an answer that is now deleted, I figured out the solution using TypeVar
, but do not fully understand, why it works. I would appreciate, if anyone could answer this better.
from typing import Any, List, TypeVar
NodeType = TypeVar('Node')
class Node:
def __init__(self, content: Any, parent: NodeType = None):
self.content = content # type: Any
self.parent = parent # type: NodeType
self.children = [] # type: List[NodeType]
if parent:
parent.children.append(self)
@property
def descendants(self) -> List[NodeType]:
"""Returns a list of nodes that are descendant from the node starting with its first child, then its
descendants (and ending with the last descendant of its last child)."""
out = []
for ch in self.children:
out.append(ch)
for desc in ch.descendants:
out.append(desc)
return out
class SpecialNode(Node):
pass
n_root = Node('foo')
n_0 = Node(10, parent=n_root)
n_01 = Node('bar', parent=n_0)
n_root_and_children = [n_root] + n_root.children
n_root_and_descendants = [n_root] + n_root.descendants
s_root = SpecialNode('special_foo')
s_0 = Node(-10, parent=s_root)
s_01 = Node('special_bar', parent=s_0)
s_root_and_children = [s_root] + s_root.children
s_root_and_descendants = [s_root] + s_root.descendants
PyCharm leaves me alone and the code works properly.