【问题标题】:Achieving interface without inheritance in Python在Python中实现没有继承的接口
【发布时间】:2022-10-24 03:30:54
【问题描述】:

我有一个排序的链表

class SortedLinkedList:
    # ...
    def insert(self, value: int):
        # ...
        if node.value > value:
            self.add_before(node, value)
        # ...

我想通过实现__gt__() 魔术方法来概括Node 只能从ints 到重载> 运算符的任何对象的值类型。

在其他语言中,我可以通过使用Interface 来实现这一点,但 Python 显然没有类似的东西。我已经看到了通过使用抽象类来伪造接口的建议,例如

class Sortable(ABC):
    @abstractmethod
    def __gt__(self, other) -> bool:
        pass

class SortedLinkedList:
    # ...
    def insert(self, value: Sortable, node: Node):
        # ...

问题是这种方法需要扩展和使用来自Sortable 的子类,这意味着不能使用已经具有> 功能(如整数)的类型

linkedlist.insert(5) # Pylance red squiggles
Argument of type "Literal[5]" cannot be assigned to
parameter "value" of type "Sortable" in function "insert"
  "Literal[5]" is incompatible with "Sortable" Pylance(reportGeneralTypeIssues) 

我知道,鉴于 Python 的动态鸭子类型和隐式风格,接口在运行前不是必需的。我不是粉丝,我选择使用typingPylance 等可用工具来实现严格类型化的开发人员体验。

我也不希望使用像.hasattr(value, '__gt__') 这样的运行时检查。我希望它在类型系统/语言服务器/IDE 级别上注册,因为表达性、可读性和 IDE 智能感知是严格类型的主要好处。

有什么办法可以做到这一点?

【问题讨论】:

    标签: python python-typing pylance


    【解决方案1】:

    你要找的是typing.Protocol

    class Sortable(Protocol):
        def __gt__(self, other) -> bool: ...
    

    这样,任何定义__gt__ 的类都将被检测为Sortable 的隐式子类型。请注意,在 Pythons >= 3.8 中,您可能需要将 other 设置为位置:

    class Sortable(Protocol):
        def __gt__(self, other, /) -> bool: ...
    

    这是因为类型检查器可能无法处理像__gt__ 这样与普通方法不同的dunder 方法,因此期望参数可能需要通过关键字调用。添加/ 告诉检查者__gt__ 的参数永远不需要通过关键字传递。相反,如果参数仅通过关键字传递,则相应地标记协议的方法:

    class Something(Protocol):
        def method(self, *, name) -> str: ...
    

    【讨论】:

    • 协议是明智地进行类型检查的方法:值得一提的是,否则 - 即在一个不使用静态类型检查的项目中,无论如何都可能只使用 < 运算符,而让错误的使用在运行时引发错误.
    • 否则,对于 issubclass 测试,Python 具有虚拟子类化的形象——从 abc.ABC 继承的类有一个“.register”类,可以做 O.P. 想要的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-17
    • 2017-06-01
    • 2018-11-23
    • 2012-04-26
    • 2016-03-07
    • 2021-03-17
    相关资源
    最近更新 更多