在具有通用类型的 class 中定义的数据 class 的类型提示
Type hints for dataclass defined inside a class with generic types
我知道标题很混乱,所以我以二叉搜索树为例:
使用普通的class定义
# 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'
上面的代码再次通过了测试,所以我认为问题出在数据class定义中对T
的引用。有谁知道将来如何解决这个问题以实现我最初的目标?
让我们从PEP 484中写的关于类型变量的作用域规则开始:
A generic class nested in another generic class cannot use same type variables. The scope of the type variables of the outer class doesn't cover the inner one:
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
这就是为什么带有嵌套装饰 class 的示例不起作用的原因。
现在让我们回答这个问题,为什么该示例使用带有 TypeVar
变量的 __init__
函数。
这是因为方法 __init__
被 mypy 视为具有独立 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
此处。
嵌套 classes 不能隐式使用包含 classes 的 TypeVar
:嵌套 class 必须是 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]'
这使得嵌套的 class well-defined 在其包含的 class 之外被引用。潜在的问题是嵌套的 class 与外部专家分开存在——推理只知道 BST.Node
或 BST.Node[T]
,而不是 BST[T].Node
.
由于嵌套不提供任何功能优势,定义单独的 classes 重用相同的 TypeVar
通常更简单
T = TypeVar('T')
class BST(Generic[T]):
root: 'Node[T]'
@dataclass
class Node(Generic[T]):
val: T
left: 'Node[T]'
right: 'Node[T]'
我知道标题很混乱,所以我以二叉搜索树为例:
使用普通的class定义
# 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'
上面的代码再次通过了测试,所以我认为问题出在数据class定义中对T
的引用。有谁知道将来如何解决这个问题以实现我最初的目标?
让我们从PEP 484中写的关于类型变量的作用域规则开始:
A generic class nested in another generic class cannot use same type variables. The scope of the type variables of the outer class doesn't cover the inner one:
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
这就是为什么带有嵌套装饰 class 的示例不起作用的原因。
现在让我们回答这个问题,为什么该示例使用带有 TypeVar
变量的 __init__
函数。
这是因为方法 __init__
被 mypy 视为具有独立 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
此处。
嵌套 classes 不能隐式使用包含 classes 的 TypeVar
:嵌套 class 必须是 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]'
这使得嵌套的 class well-defined 在其包含的 class 之外被引用。潜在的问题是嵌套的 class 与外部专家分开存在——推理只知道 BST.Node
或 BST.Node[T]
,而不是 BST[T].Node
.
由于嵌套不提供任何功能优势,定义单独的 classes 重用相同的 TypeVar
T = TypeVar('T')
class BST(Generic[T]):
root: 'Node[T]'
@dataclass
class Node(Generic[T]):
val: T
left: 'Node[T]'
right: 'Node[T]'