有没有一种方法可以安全地判断派生协议的python类的类型?

如果我使用某个方法函数签名定义协议,则隐式子类必须定义具有兼容签名的方法:

# protocols.py

from abc import abstractmethod
from dataclasses import dataclass
from typing import Protocol

class SupportsPublish(Protocol):
    @abstractmethod
    def publish(self, topic: str):
        ...

def publish(m: SupportsPublish):
    m.publish("topic")

@dataclass
class Publishable:
    foo: str = "bar"

    def publish(self):
        print(self)

publish(Publishable())

# ✗ mypy protocols.py       
# protocols.py:24: error: Argument 1 to "publish" has incompatible type "Publishable"; expected "SupportsPublish"  [arg-type]
# protocols.py:24: note: Following member(s) of "Publishable" have conflicts:
# protocols.py:24: note:     Expected:
# protocols.py:24: note:         def publish(self, topic: str) -> Any
# protocols.py:24: note:     Got:
# protocols.py:24: note:         def publish(self) -> Any
# Found 1 error in 1 file (checked 1 source file)

但是,如果我显式地子类型SupportsPublish,mypy不会报告类型错误:

...
@dataclass
class Publishable(SupportsPublish):
...

# ✗ mypy protocols.py
# Success: no issues found in 1 source file

基于来自PEP的this blurb,我预计类型判断器会发现函数签名不匹配:

请注意,显式和隐式子类型之间几乎没有区别,显式子类化的主要好处是"免费"获得一些协议方法.此外,类型判断器可以静态地验证类是否确实正确地实现了协议:

这就是我的环境:


> mypy --version
mypy 1.3.0 (compiled: yes)
> python --version
Python 3.9.17

我以为mypy会指出函数签名不匹配的问题.

推荐答案

我只想指出,正如在注释中建立的那样,如果显式地子类型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)

然后Mypy will抱怨道:

(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)

因为这是一张requirement of just regular subclassing with method overriding.

如果您不打算在完全--strict模式下运行mypy,至少要设法(通过调用它的方式或使用mypy.ini)确保您有--disallow-untyped-defs--disallow-untyped-calls

Python相关问答推荐

在使用Guouti包的Python中运行MPP模型时内存不足

如何销毁框架并使其在tkinter中看起来像以前的样子?

根据网格和相机参数渲染深度

如何计算列表列行之间的公共元素

如何使用Google Gemini API为单个提示生成多个响应?

Pythind 11无法弄清楚如何访问tuple元素

pandas DataFrame GroupBy.diff函数的意外输出

将数据框架与导入的Excel文件一起使用

为什么这个带有List输入的简单numba函数这么慢

在Python中管理打开对话框

把一个pandas文件夹从juyter笔记本放到堆栈溢出问题中的最快方法?

ThreadPoolExecutor和单个线程的超时

索引到 torch 张量,沿轴具有可变长度索引

在Python 3中,如何让客户端打开一个套接字到服务器,发送一行JSON编码的数据,读回一行JSON编码的数据,然后继续?

numpy.unique如何消除重复列?

在pandas/python中计数嵌套类别

找到相对于列表索引的当前最大值列表""

不允许 Select 北极滚动?

递归函数修饰器

Polars表达式无法访问中间列创建表达式