有没有一种方法可以安全地对协议子类的 python 类进行类型检查?
\n如果我定义具有特定方法函数签名的协议,则隐式子类必须定义具有兼容签名的方法:
\n# protocols.py\n\nfrom abc import abstractmethod\nfrom dataclasses import dataclass\nfrom typing import Protocol\n\nclass SupportsPublish(Protocol):\n @abstractmethod\n def publish(self, topic: str):\n ...\n\ndef publish(m: SupportsPublish):\n m.publish("topic")\n\n@dataclass\nclass Publishable:\n foo: str = "bar"\n\n def publish(self):\n print(self)\n\npublish(Publishable())\n\n# \xe2\x9c\x97 mypy protocols.py \n# protocols.py:24: error: Argument 1 to "publish" has incompatible type "Publishable"; expected "SupportsPublish" [arg-type]\n# protocols.py:24: note: Following member(s) of "Publishable" have conflicts:\n# protocols.py:24: note: Expected:\n# protocols.py:24: note: def publish(self, topic: str) -> Any\n# protocols.py:24: note: Got:\n# protocols.py:24: note: def publish(self) -> Any\n# Found 1 error in 1 file (checked 1 source file)\nRun Code Online (Sandbox Code Playgroud)\n但是,如果我显式 subtype SupportsPublish, mypy 不会报告类型错误:
...\n@dataclass\nclass Publishable(SupportsPublish):\n...\n\n# \xe2\x9c\x97 mypy protocols.py\n# Success: no issues found in 1 source file\nRun Code Online (Sandbox Code Playgroud)\n根据PEP 的简介,我希望类型检查器能够找到函数签名不匹配的地方:
\n\n\n请注意,显式子类型和隐式子类型之间没有什么区别,显式子类化的主要好处是免费获得一些协议方法\xe2\x80\x9c\xe2\x80\x9d。此外,类型检查器可以静态验证该类是否确实正确实现了协议:
\n
这是我的环境:
\n\n> mypy --version\nmypy 1.3.0 (compiled: yes)\n> python --version\nPython 3.9.17\nRun Code Online (Sandbox Code Playgroud)\n我希望 mypy 指出函数签名不匹配。
\n我只是想指出,正如评论中所确定的那样,如果您显式 subtype SupportsPublish, mypy 不会报告类型错误,那么实际上并不正确。
问题是你没有对你的方法进行类型注释,这本质上告诉 mypy“不要检查这个”。
如果你这样做,例如:
from dataclasses import dataclass
from typing import Protocol
class SupportsPublish(Protocol):
def publish(self, topic: str) -> None:
...
def publish(m: SupportsPublish):
m.publish("topic")
@dataclass
class Publishable:
foo: str = "bar"
def publish(self) -> None:
print(self)
Run Code Online (Sandbox Code Playgroud)
然后 mypy会抱怨:
(py311) Juans-MBP:~ juan$ mypy foo.py
foo.py:18: error: Argument 1 to "publish" has incompatible type "Publishable"; expected "SupportsPublish" [arg-type]
foo.py:18: note: Following member(s) of "Publishable" have conflicts:
foo.py:18: note: Expected:
foo.py:18: note: def publish(self, topic: str) -> None
foo.py:18: note: Got:
foo.py:18: note: def publish(self) -> None
Found 1 error in 1 file (checked 1 source file)
Run Code Online (Sandbox Code Playgroud)
因为这只是使用方法重写进行常规子类化的要求。
如果您不打算以完整--strict模式运行 mypy,至少以某种方式(通过调用它的方式或使用 a mypy.ini)确保您拥有--disallow-untyped-defs或--disallow-untyped-calls