您如何测试 python 类型协议是另一个协议的子类?

How can you test that a python typing Protocol is a subclass of another Protocol?

问题的明显解决方案是使用issubclass,但这会引发TypeError(使用Python 3.6.7),例如

>>> from typing_extensions import Protocol
>>> class ProtoSubclass(Protocol):
...     pass
... 
>>> issubclass(ProtoSubclass, Protocol)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/conda/envs/airflow/lib/python3.6/site-packages/typing_extensions.py", line 1265, in __subclasscheck__
    raise TypeError("Instance and class checks can only be used with"
TypeError: Instance and class checks can only be used with @runtime protocols
>>> from typing_extensions import runtime
>>> @runtime
... class ProtoSubclass(Protocol):
...     pass
... 
>>> issubclass(ProtoSubclass, Protocol)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/conda/envs/airflow/lib/python3.6/site-packages/typing_extensions.py", line 1265, in __subclasscheck__
    raise TypeError("Instance and class checks can only be used with"
TypeError: Instance and class checks can only be used with @runtime protocols

有关 python Protocols 主题的更多信息,请参阅

在 Python 3.6.7 中,解决这个问题的一种方法是使用 @runtime_checkable 装饰器:

>>> from typing_extensions import Protocol
>>> from typing_extensions import runtime_checkable

>>> @runtime_checkable
... class CustomProtocol(Protocol):
...     def custom(self):
...         ...
... 

>>> @runtime_checkable
... class ExtendedCustomProtocol(CustomProtocol, Protocol):
...     def extended(self):
...         ...
... 

>>> issubclass(ExtendedCustomProtocol, CustomProtocol)
True

所选答案虽然没有错,但并未反映 Protocol 的实际用途,后者提供结构子类型化。 Python 3.

始终可以使用标称子类型
from typing import Protocol, runtime_checkable

@runtime_checkable
class Closable(Protocol):
    def close(self):
        ...

class AnyClass:
    def close(self):
        ...

issubclass(AnyClass, Closable)
#> True

此外,runtime_checkable 仅在基础 class 中需要,即使被检查的 class 是 Protocol subclass.

class ExtendedProtocol(Closable, Protocol):
    ...

class AnotherProtocol(Protocol):
    def close(self):
        ...

class NotAProtocol(Closable):
    # just inherits the protocol's default implementation
    ...

issubclass(ExtendedProtocol, Closable)
#> True
issubclass(AnotherProtocol, Closable)
#> True
issubclass(NotAProtocol, Closable)
#> True