from typing import runtime_checkable @runtime_checkable
Декоратор @runtime_checkable()
модуля typing
отмечает класс протокола как протокол времени выполнения.
Такой протокол можно использовать с функциями isinstance()
и issubclass()
. Декоратор вызывает исключение TypeError
при применении его к непротокольному классу. Такое поведение позволяет выполнять простую структурную проверку, очень похожую на "актера с одной ролью" в collections.abc
, например Iterable
.
Пример:
@runtime_checkable class Closable(Protocol): def close(self): ... assert isinstance(open('/some/file'), Closable) @runtime_checkable class Named(Protocol): name: str import threading assert isinstance(threading.Thread(name='Bob'), Named)
Примечание 1. Декоратор
@runtime_checkable()
будет проверять только наличие необходимых методов или атрибутов, а не их сигнатуры или типы. Например,ssl.SSLObject
- это класс, поэтому он проходит проверкуissubclass()
дляCallable
. Однако методssl.SSLObject.__init__
существует только для того, чтобы вызватьTypeError
с более информативным сообщением, что делает невозможным вызов (создание экземпляра)ssl.SSLObject
.Примечание 2. Проверка
isinstance()
для протокола, проверяемого во время выполнения, может быть на удивление медленной по сравнению с проверкойisinstance()
для непротокольного класса. Для структурных проверок в коде, чувствительных к производительности нужно рассмотреть возможность использования альтернативных идиом, такие как вызовыhasattr()
.Изменено в Python 3.12: внутренняя реализация
isinstance()
при проверке протоколов теперь используетInspect.getattr_static()
для поиска атрибутов (ранее использовалсяhasattr()
). В результате некоторые объекты, которые раньше считались экземплярами протокола, больше не могут считаться экземплярами этого протокола в Python 3.12+, и наоборот. Это изменение вряд ли затронет большинство пользователей.Изменено в Python 3.12: члены протокола, проверяемого во время выполнения, теперь считаются "замороженными" во время выполнения, как только класс был создан. Атрибуты Monkey-Patching в протоколе, проверяемом во время выполнения, по-прежнему будут работать, но не окажут влияния на проверки
isinstance()
, сравнивающие объекты с протоколом.>>> from typing import Protocol, runtime_checkable >>> @runtime_checkable >>> class HasX(Protocol): >>> x = 1 >>> class Foo: ... >>> f = Foo() >>> isinstance(f, HasX) # False >>> f.x = 1 >>> isinstance(f, HasX) # True >>> HasX.y = 2 # без изменений, хотя HasX теперь также имеет атрибут 'y' >>> isinstance(f, HasX) # True