协议实现 class 类型检查

Protocol implementation class type check

我还没弄清楚这是否可行。

鉴于以下代码,我希望 mypy 报告两种情况的错误:

from dataclasses import dataclass
from typing import Generic, Protocol, Type, TypeVar

T = TypeVar("T")

class MyProto(Protocol):
    attr: str

class Impl:
    attr: int  # wrong type on purpose for this example

# This util class is an attempt to illustrate what I'm trying to achieve, an answer may very well
# indicate changes to it, if required, to make it work as desired.
@dataclass
class Util(Generic[T]):
   proto: T
   impl: Type[T]

# Case 1
# The following is type checked, and correctly reports the error when the implementation does not
# satisfy the protocol.
C: Type[MyProto] = Impl 

# Case 2
# Where as this is accepted regardless, but I'd like it to report error in case Impl does not
# satisfy MyProto.
util = Util(MyProto, Impl)

示例输出:

$ python -m mypy t.py
t.py:22:20: error: Incompatible types in assignment (expression has type "Type[Impl]", variable has type "Type[MyProto]")  [assignment]
    C: Type[MyProto] = Impl 
                       ^
Found 1 error in 1 file (checked 1 source file)

用例是,能够动态注册实现及其支持的协议,同时仍然进行静态类型检查。希望这是有道理的。

更新

好的,所以我发现我上面的内容应该按原样工作,只要 Protocol 有一个实际工作的类型表示。现在,T 被推断为 object 而不是 MyProto,因为这是传递给 Util() 的两种类型的最小公分母。但是,如果我明确声明 T 应该是 MyProto,我会更接近:

util = Util[MyProto](MyProto, Impl)

给我想要的,几乎:

[...]
t.py:42:22: error: Argument 1 to "Util" has incompatible type "Type[MyProto]"; expected "MyProto"  [arg-type]
    util = Util[MyProto](MyProto, Impl)
                         ^
t.py:42:31: error: Argument 2 to "Util" has incompatible type "Type[Impl]"; expected "Type[MyProto]"  [arg-type]
    util = Util[MyProto](MyProto, Impl)
                                  ^

关于参数 1 的错误是错误的,预期的类型是正确的,报告的参数类型是错误的,它应该只是 MyProto 或者协议 类 应该由类型系统。

关于参数 2 的错误是正确的,如果我修复 Impl 使其与协议匹配,该错误就会消失。

因此,当涉及到协议时,缺乏适当的类型表示和确定两种类型之间 LCD 的逻辑似乎被打破了。

有趣的是,我首先在此处发布问题后找到了答案(花了一整天的时间为此苦苦挣扎,直到我最终被推向正确的方向)。

# tweaked Util class, thus:
@dataclass
class Util(Generic[T]):
   proto: Type
   impl: Type[T]

# then using it as such, with an explicit generic type:
util = Util[MyProto](MyProto, Impl)
t.py:27:31: error: Argument 2 to "Util" has incompatible type "Type[Impl]"; expected "Type[MyProto]"  [arg-type]
    util = Util[MyProto](MyProto, Impl)
                                  ^
Found 2 errors in 1 file (checked 1 source file)

然而,这不是最优雅的,所以如果有人知道如何在构建 Util 实例时避免重复 MyProto,将不胜感激。

还注意到,如果我传入 Impl 的实例,而不仅仅是 class.

,我会从 mypy 收到更好的错误消息
t.py:27:31: error: Argument 2 to "Util" has incompatible type "Impl"; expected "MyProto"  [arg-type]
    util = Util[MyProto](MyProto, Impl())
                                  ^
t.py:27:31: note: Following member(s) of "Impl" have conflicts:
t.py:27:31: note:     attr: expected "str", got "int"