python - 确定特定对象未实现协议的原因
问题描述
考虑我定义一个协议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
.
解决方案
我认为这是由于 mypy 用于列出缺少的协议成员的启发式方法的限制。通常,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 来改进这些启发式方法?
如果您没有时间,您可以尝试获取完整列表的一种解决方法是:
- 确保 Frobbable 的所有成员都是抽象的(你已经在做)
- 使 BrokenKnob暂时成为 Frobbable 的子类
- 尝试临时创建一个新的 BrokenKnob 实例。
由此产生的错误似乎列出了所有缺少的属性而没有固定的限制。
error: Cannot instantiate abstract class 'BrokenKnob' with abstract attribute 'frob'
然后,完成修复后,您可以撤消临时更改。
推荐阅读
- android - 使用或不使用 (typeAffinity = ColumnInfo.BLOB) 在 Room 数据库中存储图像数据
- firebase - 在颤振中包含 Firebase 并在构建时出错(警告我已经粘贴了我得到的所有错误代码,所以请保持冷静)
- angular - 如何使用预定义的库或框架以角度创建微调器日期选择器(可滚动日期选择器)?
- java - 有没有在java中获取设备名称的函数?
- node.js - 如何在 Azure 函数中读取 Nodejs 中的 .env 文件变量
- python - 为什么 Python 3.9 中 SyntaxErrors 的错误消息与以前的 Python 版本不同?
- javascript - 移除带有属性 JS 的元素
- python - 气流弹性搜索钩子
- java - java - 如何在java的全局DbConnection中使用准备好的sql语句?
- c# - 搜索文件并删除它,不知道路径 C#