确定特定对象不实现协议的原因

2024-06-17 13:32:57 发布

您现在位置:Python中文网/ 问答频道 /正文

考虑我定义一个协议^ {CD1>}。此外,我还有一个协议的有效实现,以及一个缺少.frob()方法的中断实现:

from typing import Protocol
from abc import abstractmethod


class Frobbable(Protocol):
    @abstractmethod
    def frob(self) -> None:
        raise NotImplementedError


def main(knob: Frobbable) -> None:
    knob.frob()


class Knob:
    def frob(self) -> None:
        print("knob has been frobbed")


class BrokenKnob:
    pass


main(Knob())
main(BrokenKnob())

使用mypy检查此程序会导致错误,如预期的那样:

testprotocol.py:25:6: error: Argument 1 to "main" has incompatible type "BrokenKnob"; expected "Frobbable"  [arg-type]
    main(BrokenKnob())
         ^
Found 1 error in 1 file (checked 1 source file)

不幸的是,它没有提供任何关于为什么BrokenKnob不兼容的信息:在本例中,它缺少.frob()。如果没有这些信息,在一个非平凡的程序中纠正这样一个问题(使用具有许多方法和许多实现的协议)将成为一项极其繁琐的工作

是否有任何方法可以在不修改程序的情况下从mypy或任何其他工具获取此信息?我知道我可以显式地将Frobbable子类化,但这与使用Protocol的目的背道而驰


Tags: 方法fromimport程序none信息协议main
1条回答
网友
1楼 · 发布于 2024-06-17 13:32:57

我认为这是由于mypy用于列出missing protocol members的启发式方法的限制。通常情况下,mypy应该报告任何丢失的协议成员,但为了避免生成过多的垃圾邮件错误消息,如果每个协议成员都丢失或丢失的成员数超过2,则不必麻烦报告

例如,如果我们调整您的示例,使其符合这些约束条件

from typing import Protocol
from abc import abstractmethod


class Frobbable(Protocol):
    @abstractmethod
    def frob(self) -> None:
        raise NotImplementedError
    @abstractmethod
    def bob(self) -> None:
        raise NotImplementedError


def main(knob: Frobbable) -> None:
    knob.frob()


class BrokenKnob:
    def frob(self) -> None:
        raise NotImplementedError


main(BrokenKnob())

…我们得到了预期的更具描述性的错误消息:

test.py:23: error: Argument 1 to "main" has incompatible type "BrokenKnob"; expected "Frobbable"
test.py:23: note: 'BrokenKnob' is missing following 'Frobbable' protocol member:
test.py:23: note:     bob

虽然这些启发式方法看起来是合理的,但我也认为它们可能需要一些改进,以便更好地处理您遇到的用例。例如,如果所有成员都丢失了,我认为mypy应该报告一条“此对象未在协议中实现任何内容”错误消息,而不是更一般的错误消息,并可能更优雅地处理丢失成员过多的情况。如果你愿意的话,你可以尝试提交一份PR来改进这些启发法

如果您没有时间,您可以尝试做一个变通方法来获取完整列表:

  1. 确保Frobbable的所有成员都是抽象的(您已经在这样做了)
  2. 使BrokenKnob暂时子类可起泡
  3. 尝试临时创建BrokenKnob的新实例

由此产生的错误似乎列出了所有缺少的属性,而没有固定的限制

error: Cannot instantiate abstract class 'BrokenKnob' with abstract attribute 'frob'

然后,一旦完成修复,就可以撤消临时更改

相关问题 更多 >