If Foo
是通用的T
, U
and Bar
是通用的V
但存储一个实例Foo
,然后避免写入
class Bar(Generic[T, U, V]):
def __init__(self, foo: Foo[T, U], v: V):
...
我们可以定义FooT = TypeVar("FooT", Foo[Any, Any])
和写
class Bar(Generic[FooT, V]):
def __init__(self, foo: FooT, v: V):
pass
然后实例化Bar
变成Bar[Foo[int, str], bool]
例如代替Bar[int, str, bool]
。对我来说,这是代码清晰度和可维护性方面的改进。但即使有一个更简单的玩具示例,我也会遇到这种模式的问题:
from typing import Generic, TypeVar, Union, TYPE_CHECKING
ResultT = TypeVar("ResultT")
class Query(Generic[ResultT]):
def __init__(self, r: ResultT) -> None:
self.r = r
QueryT = TypeVar("QueryT", bound=Query[Any])
class Client(Generic[QueryT]):
def __init__(self, q: QueryT) -> None:
self.q = q
self.r = q.r
c = Client[Query[int]](Query[int](2))
if TYPE_CHECKING:
reveal_type(c.q)
reveal_type(c.q.r)
reveal_type(c.r)
print(id(c.r) == id(c.q.r)) # true
----
mypytest.py:17: note: Revealed type is "mypytest.Query[builtins.int]"
mypytest.py:18: note: Revealed type is "builtins.int"
mypytest.py:19: note: Revealed type is "Any"
Success: no issues found in 1 source file
我很惊讶 mypy 的推断Any
在这种情况下当c.q.r
and c.r
是相同的。基本上是假设c.r
是上界ResultT
这是Any
在这种情况下,而不是实例化所隐含的类型Query[int]
。但令我困扰的是,类型根据访问模式正确解析......
一些其他有趣的修改(这可能说明我对使用 Python 泛型相对较新),基于我是否更改QueryT
定义并运行mypy
with --strict
:
QueryT = TypeVar("QueryT", bound=Query[Union[int, str]])
---
mypytest.py:22: error: Value of type variable "QueryT" of "Client" cannot be "Query[int]"
mypytest.py:24: note: Revealed type is "mypytest.Query[builtins.int]"
mypytest.py:25: note: Revealed type is "builtins.int"
mypytest.py:26: note: Revealed type is "Union[builtins.int, builtins.str]"
这与上面的行为基本相同,但现在上限是Union[int, str]
正如预期的那样(但同样,为什么类型现在缩小为int
?)。我在严格模式下也遇到错误——我认为这个分配会被允许吗?
QueryT = TypeVar("QueryT", bound=Union[Query[int], Query[str]])
---
mypytest.py:18: note: Revealed type is "mypytest.Query[builtins.int]"
mypytest.py:19: note: Revealed type is "builtins.int"
mypytest.py:20: note: Revealed type is "Union[builtins.int, builtins.str]"
该错误通过分解出Union
。我相信这个解释是相当明显的,只需要考虑一下即可。
QueryT = TypeVar("QueryT", Query[int], Query[str])
---
mypytest.py:24: error: Need type annotation for "q"
mypytest.py:25: error: Need type annotation for "r"
mypytest.py:29: note: Revealed type is "Any"
mypytest.py:30: note: Revealed type is "Any"
mypytest.py:31: note: Revealed type is "Any"
将 TypeVar 限制为两种类型的这种简单情况似乎是最容易推理的,但它给类型检查器带来了最多的问题。为什么这里需要显式注释以避免推断Any
?
只需跳过中间部分QueryT
类型,在严格的情况下一切正常,没有错误:
class Client(Generic[ResultT]):
def __init__(self, q: Query[ResultT]) -> None:
...
c = Client[int](Query[int](2))
...
---
mypytest.py:39: note: Revealed type is "mypytest.Query[builtins.int]"
mypytest.py:40: note: Revealed type is "builtins.int"
mypytest.py:41: note: Revealed type is "builtins.int"
如果缺乏对正在发生的事情的深入了解/对最佳实践的建议,我可能只会收集并重复用于类的所有原始泛型类型; mypy 已经强制您为继承执行此操作,因此对于组合来说可能不是什么大问题。