确定特定对象为何未实现协议

问题描述 投票:3回答:1

考虑我定义了协议Frobbable。此外,我还有该协议的有效实现,还有一个缺少.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的目的。

python mypy
1个回答
2
投票

我认为这是由于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. 子类易碎
  4. 尝试临时创建BrokenKnob的新实例。
  5. 产生的错误似乎列出了所有缺少的属性,而没有固定的限制。

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

然后,完成修复后,您可以撤消临时更改。

© www.soinside.com 2019 - 2024. All rights reserved.