Python 自定义鸭子类型的类型注释

Python type annotation for custom duck type

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

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

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

可以定义一个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, you can use it in isinstance() and issubclass() tests, which is more than good enough for tools like mypy:

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

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