Аннотация типа Python для пользовательского типа утки

Питона typing модуль определяет количество типов утки, например, typing.SupportsAbs представлять любой тип, который реализует __abs__ особый метод.

Можно ли определить пользовательские типы утки таким образом, чтобы я мог использовать их в качестве допустимых аннотаций типов?

Например, я хотел бы иметь возможность комментировать, что аргумент должен быть эквивалентом утиного типа threading.Lockлюбой объект, реализующий acquire а также release методы. В идеале я мог бы прокомментировать такой аргумент, как SupportsAcquireAndRequire или же DuckLock, скорее, чем object,

1 ответ

Решение

Вы можете определить абстрактный базовый класс (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__ метод, вы можете использовать его в isinstance() а также issubclass() тесты, что более чем достаточно для таких инструментов, как 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:
        ...

Тебе нужно @runtimecheckable если вы хотите иметь возможность использовать isinstance как в ответе Мартейн.

>>> from threading import Lock
>>> isinstance(Lock(), RuntimeCheckable)
True
Другие вопросы по тегам