使用 TypeVar 与另一个泛型类的绑定来泛化一个类

2024-04-27

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 已经强制您为继承执行此操作,因此对于组合来说可能不是什么大问题。


None

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 TypeVar 与另一个泛型类的绑定来泛化一个类 的相关文章

随机推荐