在2017年PyCharm的Win10x64下使用Python 3.6。
我知道在Python社区中输入(甚至类型提示)是不受欢迎的,但请耐心等待。
假设我有一个Node
类来创建具有任意内容的树状容器。这是一个高度简化的版本:
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
我正在尝试使用正确的类型提示。鉴于我需要指出parent
的类型为Node
,我使用PEP 484中规定的引号作为前向引用。与self.children
相同,使用List['Node']
,也使用属性descendants
。
如果我继承自Node(就像class SpecialNode(Node): pass
那样),我希望PyCharm的类型检查器能够正确地推断出所有相关类型,这样就可以预期SpecialNode
Node
。
考虑一下:
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在最后一行中强调s_root.descendants
并告诉我:
预期类型'列表[SpecialNode]' (匹配的通用类型'列表[TypeVar(' _T')]'),得到'列表[节点]'代替
我做错了什么?
修改
受到现在删除的答案的启发,我使用TypeVar
找出了解决方案,但没有完全理解,为什么会有效。如果有人能更好地回答这个问题,我将不胜感激。
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让我独自一人,代码正常运行。
答案 0 :(得分:0)
您刚刚将NodeType的类型设为未知.... NodeType从不存在集合类型。我认为只是混淆pycharm并不是很难。 Mypy仍然给我一大堆问题:
node.py:3: error: String argument 1 'Node' to TypeVar(...) does not match variable name 'NodeType'
node.py:3: error: "object" not callable
node.py:8: error: Invalid type "app.services.node.NodeType"
node.py:8: error: The return type of "__init__" must be None
node.py:10: error: Invalid type "app.services.node.NodeType"
node.py:11: error: Invalid type "app.services.node.NodeType"
node.py:13: error: object has no attribute "children"
node.py:15: error: Invalid type "app.services.node.NodeType"
node.py:20: error: Need type annotation for variable
我可以通过这样编写来解决大多数问题:
from typing import Any, List, TypeVar, Generic, Optional
T = TypeVar('T', bound="Node")
class Node(Generic[T]):
def __init__(self, content: Any, parent: Optional[T]=None) -> None:
self.content = content # type: Any
self.parent = parent # type: Optional[T]
self.children = [] # type: List[T]
if parent:
parent.children.append(self)
@property
def descendants(self) -> List[T]:
"""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[Node]('foo')
n_0 = Node[Node](10, parent=n_root)
n_01 = Node[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[SpecialNode](-10, parent=s_root)
s_01 = Node[Node]('special_bar', parent=s_0)
s_root_and_children = [s_root] + s_root.children
s_root_and_descendants = [s_root] + s_root.descendants
但是这给我留下了node.py:12:错误:参数1到"追加" " list"具有不兼容的类型Node [T];期待" T" 但是pycharm接受了它......
这里发生的事情是Node的存储类型也是Node但可以被子类型否决,但这意味着它是动态类型的,所以我们应该只使用类似的东西:Any [T]而不是T?
任何帮助将不胜感激!
P.S:这不是解决这个问题的答案,只是解答为什么它似乎得到解决。
答案 1 :(得分:0)
关于您的编辑,PyCharm似乎在做自己的事情,因为mypy
不喜欢此代码:
(mytest) bash-3.2$ mypy test2.py
test2.py:5: error: String argument 1 'Node' to TypeVar(...) does not match variable name 'NodeType'
test2.py:5: error: "object" not callable
test2.py:10: error: Invalid type "test2.NodeType"
test2.py:12: error: Invalid type "test2.NodeType"
test2.py:13: error: Invalid type "test2.NodeType"
test2.py:15: error: NodeType? has no attribute "children"
test2.py:18: error: Invalid type "test2.NodeType"
test2.py:22: error: Need type annotation for 'ch'
(mytest) bash-3.2$
这是在mypy 0.641
和Python 3.7.1
上。
此后您是否重新访问了此编辑/修复程序?