问题描述
我知道标题很混乱,所以让我以二进制搜索树为例:
使用普通的类定义
# This code passed mypy test
from typing import Generic,TypeVar
T = TypeVar('T')
class BST(Generic[T]):
class Node:
def __init__(
self,val: T,left: 'BST.Node',right: 'BST.Node'
) -> None:
self.val = val
self.left = left
self.right = right
以上代码通过了mypy
测试。
使用dataclass
但是,当我尝试使用dataclass
简化Node
的定义时,代码在mypy测试中失败了。
# This code failed to pass mypy test
from dataclasses import dataclass
from typing import Generic,TypeVar
T = TypeVar('T')
class BST(Generic[T]):
@dataclass
class Node:
val: T
left: 'BST.Node'
right: 'BST.Node'
mypy
给了我这个错误消息:(test_typing.py:8
是第val: T
行)
test_typing.py:8: error: Type variable "test_typing.T" is unbound
test_typing.py:8: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class)
test_typing.py:8: note: (Hint: Use "T" in function signature to bind "T" inside a function)
指出问题所在
# This code passed mypy test,suggest the problem is the reference to `T` in the dataclass definition
from dataclasses import dataclass
from typing import Generic,TypeVar
T = TypeVar('T')
class BST(Generic[T]):
@dataclass
class Node:
val: int # chose `int` just for testing
left: 'BST.Node'
right: 'BST.Node'
以上代码再次通过了测试,因此我认为问题在于数据类定义中对T
的引用。有人知道将来如何解决此问题以实现我的最初目标吗?
解决方法
让我们从PEP 484中写的有关类型变量的作用域规则的内容开始:
嵌套在另一个通用类中的通用类不能使用相同类型的变量。 外部类的类型变量的范围不覆盖内部的:
T = TypeVar('T') S = TypeVar('S') class Outer(Generic[T]): class Bad(Iterable[T]): # Error ... class AlsoBad: x = None # type: List[T] # Also an error class Inner(Iterable[S]): # OK ... attr = None # type: Inner[T] # Also OK
这就是为什么您的带有嵌套装饰类的示例不起作用的原因。
现在让我们回答一个问题,为什么该示例与带有__init__
变量的TypeVar
函数一起使用。
这是因为mypy将方法__init__
视为具有独立TypeVar
变量的通用方法。例如,reveal_type(BST[int].Node.__init__)
显示Revealed type is 'def [T,T] (self: main.BST.Node,val: T'-1,left: main.BST.Node,right: main.BST.Node)'
。即T
在此处未绑定到int
。
嵌套类不能从其包含的类中隐式使用TypeVar
:嵌套类必须是Generic
,并且绑定的TypeVar
。
BT = TypeVar('BT')
NT = TypeVar('NT')
class BST(Generic[BT]):
root: 'BST.Node[BT]' # root note is of same type as search tree
@dataclass
class Node(Generic[NT]): # generic node may be of any type
val: NT
left: 'BST.Node[NT]'
right: 'BST.Node[NT]'
当嵌套类在其包含类之外进行引用时,这将使嵌套类具有良好的定义。潜在的问题是嵌套类与外部专家分开存在–推论仅知道BST.Node
或BST.Node[T]
,而不知道BST[T].Node
。
由于嵌套没有提供任何功能优势,因此使用相同的TypeVar
T = TypeVar('T')
class BST(Generic[T]):
root: 'Node[T]'
@dataclass
class Node(Generic[T]):
val: T
left: 'Node[T]'
right: 'Node[T]'