自定义duck类型的Python类型注释

sho*_*yer 3 python duck-typing type-hinting python-3.x

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

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

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

mod*_*itt 8

typing已更新以支持像这样的用例 - 您可以用于typing.Protocol此目的。

from typing import Protocol, runtime_checkable

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

    def release(self) -> None:
        ...
Run Code Online (Sandbox Code Playgroud)

@runtimecheckable如果您希望能够使用isinstanceMartijn 的答案,则需要。

>>> from threading import Lock
>>> isinstance(Lock(), RuntimeCheckable)
True
Run Code Online (Sandbox Code Playgroud)


Mar*_*ers 6

您可以定义一个抽象基类(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
Run Code Online (Sandbox Code Playgroud)

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

通过给ABC一个__subclasshook__方法,你可以使用它isinstance()和issubclass()测试,这对于像以下工具来说已经足够好了mypy:

>>> from threading import Lock
>>> isinstance(Lock(), SupportsAcquireAndRequire)
True
Run Code Online (Sandbox Code Playgroud)

  • 看起来这是我们现在可以做的最好的事情 - 这实际上仍然是`打字'的一个悬而未决的问题:https://github.com/python/typing/issues/11 (3认同)