Python中类型注释的自引用或前向引用

47
我正在尝试弄清楚类型的自引用与Python3的类型注释如何配合使用 - 文档没有关于此的具体说明。
例如:
from typing import TypeVar, Optional, Generic

T = TypeVar('T')
class Node(Generic[T]):
    left = None
    right = None
    value = None

    def __init__(
        self, value: Optional[T],
        left: Optional[Node[T]]=None,
        right: Optional[Node[T]]=None,
    ) -> None:
        self.value = value
        self.left = left
        self.right = right

这段代码产生了错误:

Traceback (most recent call last):
  File "node.py", line 4, in <module>
    class Node(Generic[T]):
  File "node.py", line 12, in Node
    right: Optional[Node[T]]=None,
NameError: name 'Node' is not defined

这是使用Python 3.5.1


嗯,typing 仅在 3.5 及以上版本可用。查看 3.5 版本的更新内容 - thefourtheye
@thefourtheye:我编辑了问题。我刚安装了3.5.1,但问题仍然存在。我之前能用3.4是因为我已经安装了mypy。 - LiraNuna
1个回答

55

PEP 0484 - 类型提示 - 前向声明的问题解决了这个问题:

类型提示的问题在于注释(参见PEP 3107,与默认值类似)在函数定义时被评估,因此任何在注释中使用的名称必须在函数定义时已经定义。一个常见的情况是类定义需要在它们的注释中引用类本身的方法。(更一般地,也可能发生在相互递归的类中。)这对于容器类型来说很自然,例如:...
如上所述,这样写是行不通的,因为 Python 中的类名在整个类体执行完后才会被定义。我们的解决方案并不特别优雅,但可以完成工作,允许在注释中使用字符串字面量。尽管如此,大多数情况下你不需要使用它——类型提示的大多数用途都预期引用内置类型或在其他模块中定义的类型。
from typing import TypeVar, Optional, Generic

T = TypeVar('T')
class Node(Generic[T]):
    left = None
    right = None
    value = None

    def __init__(
        self,
        value: Optional[T],
        left: Optional['Node[T]']=None,
        right: Optional['Node[T]']=None,
    ) -> None:
        self.value = value
        self.left = left
        self.right = right

>>> import typing
>>> typing.get_type_hints(Node.__init__)
{'return': None,
 'value': typing.Union[~T, NoneType],
 'left': typing.Union[__main__.Node[~T], NoneType],
 'right': typing.Union[__main__.Node[~T], NoneType]}

谢谢!我简直不敢相信我完全错过了那部分——它甚至有一个类似于我的例子! - LiraNuna
那么自引用的字典怎么办呢?例如:MyDict=TypedDict('MyDict',{'callback': Callable[[MyDict], None]}。我想要做的是在一个字典中添加一个键/值,该值是一个回调函数,以MyDict本身作为第一个参数... - Melardev
6
如果你正在使用Python 3.7以上版本,from __future__ import annotations 可以让你使用 Optional[Node[T]]。详情请见:https://www.python.org/dev/peps/pep-0563/。 - falsetru
@falsetru,您是正确的,将类型放在注释中是一种与我最初想法不同的方法。 - Sergey Grechin
1
关闭mypy的警告/错误不是解决类型问题的好方法。 - Jens
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接