使用与另一个通用类绑定的类型var通用类
如果foo
在t
中是通用存储一个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在这种情况下推断cqr
和cr 。 >是相同的。基本上,它假设
cr
是resultt
的上限,在这种情况下是任何
,而不是通过实例化的实例化所隐含类型查询[int]
。但是令我困扰的是,该类型是根据访问模式正确解决的……
其他一些有趣的修改(这可能与我相对较新的方式与Python的仿制药一起工作有关),请根据我是否更改了queryt
定义并运行mypy
带有- 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限制为两种类型的简单案例似乎是最简单的建议,但是它给了类型的检查器最大的问题。为什么在这里需要明确的注释以避免推断 ?
通过仅跳过中间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已经迫使您为继承而这样做,因此对于构图来说可能没什么大不了的。
If Foo
is generic in T
, U
and Bar
is generic in V
but stores an instance of Foo
, then to avoid writing
class Bar(Generic[T, U, V]):
def __init__(self, foo: Foo[T, U], v: V):
...
we could define FooT = TypeVar("FooT", Foo[Any, Any])
and write
class Bar(Generic[FooT, V]):
def __init__(self, foo: FooT, v: V):
pass
Then instantiating Bar
becomes Bar[Foo[int, str], bool]
for example instead of Bar[int, str, bool]
. To me this is an improvement in terms of code clarity and maintainability. But even with a simpler toy example, I run into problems with this pattern:
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
I am surprised mypy is inferring Any
in this situation when c.q.r
and c.r
are identical. Basically it is assuming c.r
is the upper bound of ResultT
which is Any
in this case, instead of the type implied by instantiation of Query[int]
. But what bothers me is the type is correctly resolved depending on the access pattern...
Some other interesting modifications (which probably speak to my relatively newness to working with Python's generics), based on if I change the QueryT
definitions and run 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]"
This is basically the same behavior as above, but now the upper bound is Union[int, str]
as expected (but again, why is the type now narrowed to int
?). I also get an error in strict mode -- I thought this assignment would be allowed?
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]"
The error is resolved by factoring out the Union
. I'm sure the explanation is fairly obvious, just need to think about it.
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"
This simple case of restricting the TypeVar to two types seems the simplest to reason about, yet it gives the type checker the most problems. Why does it need an explicit annotation here to avoid inferring Any
?
By just skipping the intermediate QueryT
type, everything works w/ no errors under strict:
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"
Absent some insight into what's going on / advice on best practices, I will probably just collect and repeat all the primitive generic types in use for a class; mypy already forces you to do this for inheritance, so probably not a big deal for composition.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论