【问题标题】:Python type annotation for custom duck type自定义鸭子类型的 Python 类型注释
【发布时间】:2016-11-07 21:01:53
【问题描述】:

Python 的 typing 模块定义了许多鸭子类型,例如,typing.SupportsAbs 来表示任何实现 __abs__ 特殊方法的类型。

是否可以以某种方式定义自定义鸭子类型,以便我可以将它们用作有效的类型注释?

例如,我希望能够注释一个参数应该是 threading.Lock 的鸭子类型等价物,即任何实现 acquirerelease 方法的对象。理想情况下,我可以将这样的论点注释为SupportsAcquireAndRequireDuckLock,而不是object

【问题讨论】:

  • 你看过它们是如何定义的吗?你可以很容易地做类似的事情。
  • 确实,SupportsAbs 只是继承自私有typing._Protocol 的几行代码。有没有不涉及使用私有 API 的方法?

标签: python python-3.x type-hinting duck-typing


【解决方案1】:

typing 已更新以支持像这样的用例 - 您可以为此使用 typing.Protocol

from typing import Protocol, runtime_checkable

@runtime_checkable
class LockLike(Protocol):
    def acquire(self) -> None:
        ...

    def release(self) -> None:
        ...

如果您想像 Martijn 的回答那样使用 isinstance,则需要 @runtimecheckable

>>> from threading import Lock
>>> isinstance(Lock(), RuntimeCheckable)
True

【讨论】:

    【解决方案2】:

    你可以定义一个abstract base class (ABC)来指定接口:

    from abc import ABCMeta, abstractmethod
    
    class SupportsAcquireAndRequire(metaclass=ABCMeta):
        @abstractmethod
        def acquire(self):
            pass
    
        @abstractmethod
        def release(self):
            pass
    
        @classmethod
        def __subclasshook__(cls, C):
            for method in ('release', 'acquire'):
                for B in C.__mro__:
                    if method in B.__dict__:
                        if B.__dict__[method] is None:
                            return NotImplemented
                        break
                else:
                    return NotImplemented
            return True
    

    这基本上是协议(如typing.SupportsAbs)的实现方式,尽管没有直接使用ABCMeta

    通过为 ABC 提供 __subclasshook__ method,您可以在 isinstance()issubclass() 测试中使用它,这对于像 mypy 这样的工具来说已经足够了:

    >>> from threading import Lock
    >>> isinstance(Lock(), SupportsAcquireAndRequire)
    True
    

    【讨论】:

    • 看起来这是我们目前能做的最好的事情——这实际上对于typing来说仍然是一个悬而未决的问题:github.com/python/typing/issues/11
    • @shoyer:是的,这就是为什么我链接到的 mypy 文档提到对鸭子类型的更明确支持仍然即将到来。在那之前,ABCMeta 是必经之路。
    • 知道为什么我们不在运行时检查方法 signatures 而只检查方法 names(在 __subclasshook__ 中实现在 collections.abctyping)?它使结构子类型的动态视图(部分类型检查)与静态视图(完整类型检查)不一致。
    • @Maggyero 它与 ABC 的工作方式是一致的,它们只强制执行名称。
    • @MartijnPieters +1 你是对的。首先abc.ABCMeta 将其实例上的__abstractmethods__ 属性设置为方法名称的frozenset,其中__isabstractmethod__ 属性设置为True。然后object.__new__ 检查abc.ABCMeta 实例在它们自己实例化时没有将__abstractmethods__ 属性设置为str 的非空迭代,因为只能实例化非抽象类。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-24
    • 2015-05-21
    • 1970-01-01
    • 2016-09-10
    • 2018-10-11
    • 2013-06-06
    • 2011-03-23
    相关资源
    最近更新 更多