该问题的明显解决方案是使用
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 3.6 中使用
issubclass
直接检查协议子类关系会引发
TypeError
。这是因为在 Python 3.7 之前的版本中,协议的子类检查机制尚未完全实现。
不过,你可以通过以下两种方式来变通解决这个问题:
1. 使用
typing_inspect
库:
typing_inspect
库提供了一些函数来检查类型提示,包括协议。你可以使用
typing_inspect.is_subtype
函数来检查一个协议是否是另一个协议的子类型。
from typing_extensions import Protocol, runtime
from typing_inspect import is_subtype
@runtime
class ProtoBase(Protocol):
def method_base(self): ...
@runtime
class ProtoSubclass(ProtoBase):
def method_sub(self): ...
print(is_subtype(ProtoSubclass, ProtoBase)) # 输出:True
print(is_subtype(ProtoBase, ProtoSubclass)) # 输出:False
2. 使用自定义函数检查协议成员:
由于协议本质上是定义了一组方法,你可以编写一个自定义函数来检查一个协议是否包含另一个协议的所有方法。
from typing_extensions import Protocol, runtime
@runtime
class ProtoBase(Protocol):
def method_base(self): ...
@runtime
class ProtoSubclass(ProtoBase):
def method_sub(self): ...
def is_protocol_subclass(sub_proto, base_proto):
base_methods = set(getattr(base_proto, '__abstractmethods__', []))
sub_methods = set(getattr(sub_proto, '__abstractmethods__', []))
return base_methods.issubset(sub_methods)
print(is_protocol_subclass(ProtoSubclass, ProtoBase)) # 输出:True
print(is_protocol_subclass(ProtoBase, ProtoSubclass)) # 输出:False
需要注意的是,这两种方法都有一些局限性。
typing_inspect
库需要额外安装,而自定义函数则需要你手动维护协议方法的检查逻辑。
总的来说,如果你正在使用 Python 3.7 或更高版本,那么
issubclass
函数可以直接用于检查协议的子类关系。但如果你必须使用 Python 3.6,那么以上两种方法可以帮助你解决这个问题。