为什么在isinstance() 中可以对一个非运行时可检查的协议(该协议包含非方法成员)进行检查?
我有如下代码
from typing import Protocol, runtime_checkable
@runtime_checkable
class A(Protocol):
def f(self): ...
class B:
def f(self): ...
if __name__ == '__main__':
assert isinstance(B(), A)
如果我移除runtime_checkable装饰器,会得到如下异常:
TypeError: Instance and class checks can only be used with @runtime_checkable protocols
然而,如果我为类A 增加一个属性,只要我使用的是Python 3.9或更早版本,就不需要这个装饰器。
class A(Protocol):
x: int
def f(self): ...
结果:
>>> assert isinstance(B(), A)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
包含至少一个非方法成员的协议被称为数据协议(typing.python.org)。为什么数据协议不需要runtime_checkable装饰器?我在文档中找不到相关内容。
没有属性时(3.9):
Python 3.9.13 (tags/v3.9.13:6de2ca5, May 17 2022, 16:36:42) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from typing import Protocol
>>> class A(Protocol):
... def f(self): ...
...
>>> class B:
... def f(self): ...
...
>>> assert isinstance(B(), A)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\typing.py", line 1146, in __instancecheck__
issubclass(instance.__class__, cls)):
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\abc.py", line 123, in __subclasscheck__
return _abc_subclasscheck(cls, subclass)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\typing.py", line 1208, in _proto_hook
raise TypeError("Instance and class checks can only be used with"
TypeError: Instance and class checks can only be used with @runtime_checkable protocols
有属性时(3.9):
Python 3.9.13 (tags/v3.9.13:6de2ca5, May 17 2022, 16:36:42) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from typing import Protocol
>>> class A(Protocol):
... x: int
... def f(self): ...
...
>>> class B:
... def f(self): ...
...
>>> assert isinstance(B(), A)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
使用Python版本 ≥ 3.10时,我得到TypeError:
Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr 5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from typing import Protocol
>>> class A(Protocol):
... x: int
... def f(self): ...
...
>>> class B:
... def f(self): ...
...
>>> assert isinstance(B(), A)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\lib\typing.py", line 1498, in __instancecheck__
raise TypeError("Instance and class checks can only be used with"
TypeError: Instance and class checks can only be used with @runtime_checkable protocols
解决方案
恭喜,你刚刚重新发现了Python的 typing模块中的一个六年前的bug。它在Python 3.9及更新版本中被修复,但随后在3.9中被回滚,因为该变更与补丁版本的语义化版本号要求不兼容。
你可以在 这个GitHub问题 中追踪这个bug的历史,它在3.10的发行说明中也有提及(这里的第10点)。
Python 3.9已正式退休(即EOL),所以这个修复不会回移植到3.9或更早的版本。目前受支持的Python版本都不受此bug影响。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。