首页 > 解决方案 > 如何让 Mypy 使用多个相互依赖的 mixin?

问题描述

目前在 Electrum 中,我们使用Unionon 类型self来访问来自多个混合父类的方法。例如,QtPluginBase依靠混入一个子类HW_PluginBase来工作。例如,有效的用法是class TrezorPlugin(QtPluginBase, HW_PluginBase).

有 Qt gui、Kivy gui,还有 CLI。虽然没有为 Kivy 实现硬件钱包,但它们可能会在未来实现。您已经可以在 CLI 上使用它们。

然而,也有多家硬件钱包制造商,都有自己的插件。

考虑 Trezor + Qt:

对于 Qt,我们有这样的类层次结构:

对于 Trezor,我们有:

并创建实际的 Qt Trezor 插件:

关键是基本的 gui-neutral 插件将首先获得制造商特定的方法;然后它将获得特定于gui的方法。

Aaron(在评论中)建议QtPluginBase可以子类HW_PluginBase化,但这意味着制造商特定的东西会出现,这意味着 CLI 或 Kivy 不能使用生成的类。

请注意,两者

electrum.plugins.trezor.trezor.TrezorPlugin(HW_PluginBase)

electrum.plugins.hw_wallet.qt.QtPluginBase

依靠HW_PluginBase。他们不能都继承它。

因此,如果我们避免混入,那么唯一的选择是要么有QtPluginBase子类TrezorPlugin(但有很多制造商),要么TrezorPlugin可以子类QtPluginBase但同样,生成的类不能被 CLI 或 Kivy 使用。

我意识到这Union是一个“或”,所以这个提示确实没有意义。但是没有Intersection类型。使用 Union,大部分 PyCharm 功能都可以工作。

一件很好的事情是,如果QtPluginBase可以有一个类型提示,它是子类HW_PluginBase,但实际上在运行时没有子类化。

怎么能用 Mypy 输入,而不必Union在每个方法上使用这个 hacky 类型提示(因为每个方法都有self)?

标签: pythonpython-3.xmultiple-inheritancegeneric-programmingmypy

解决方案


使用PEP-544 (Python 3.8+) 中添加的协议,您可以自己定义交集接口!ClassA这也使您可以隐藏您不想使用的实现细节ClassB

from typing import Protocol

class InterfaceAB(Protocol):
    def method_a(self) -> None: ...
    def method_b(self) -> None: ...

class ClassA:
    def method_a(self) -> None:
        print("a")

class ClassB:
    def method_b(self: InterfaceAB) -> None:
        print("b")
        self.method_a()

# if I remove ClassA here, I get a type checking error!
class AB(ClassA, ClassB): pass

ab = AB()
ab.method_b()

# % mypy --version
# mypy 0.761
# % mypy mypy-protocol-demo.py
# Success: no issues found in 1 source file

此文件的初始版本归功于 SomberNight/ghost43。


推荐阅读