在 Python 中 issubclass() 意外地抱怨“Protocols with non-method members”

问题描述 投票:0回答:2

我尝试了明显的方法来检查我的协议:

from typing import Any, Protocol, runtime_checkable

@runtime_checkable
class SupportsComparison(Protocol):
    def __eq__(self, other: Any) -> bool:
        ...

issubclass(int, SupportsComparison)

不幸的是,

issubclass()
调用以异常结束(Ubuntu 22.04 中的 Python 3.10.6):

$ python3.10 protocol_test.py
Traceback (most recent call last):
  File "protocol_test.py", line 8, in <module>
    issubclass(object, SupportsComparison)
  File "/usr/lib/python3.10/abc.py", line 123, in __subclasscheck__
    return _abc_subclasscheck(cls, subclass)
  File "/usr/lib/python3.10/typing.py", line 1570, in _proto_hook
    raise TypeError("Protocols with non-method members"
TypeError: Protocols with non-method members don't support issubclass()

如您所见,我没有向

SupportsComparison
添加任何非方法成员。这是标准库中的错误吗?

python python-3.x protocols python-typing
2个回答
3
投票

来自文档

一个重写

__eq__()
并且没有定义
__hash__()
的类 将其
__hash__()
隐式设置为
None
.

因此,在您的情况下,您有隐式非方法成员

SupportsComparison.__hash__ = None
。您可以通过明确声明
__hash__
来修复它:

from typing import Any, Protocol, runtime_checkable

@runtime_checkable
class SupportsComparison(Protocol):
    def __eq__(self, other: Any) -> bool:
        ...

    def __hash__(self) -> int:
        ...

issubclass(int, SupportsComparison)

-3
投票

您看到的错误消息与您已将非方法成员添加到 SupportsComparison 协议这一事实无关。相反,问题在于您在协议上使用了

@runtime_checkable
装饰器,这向 Python 表明可以在运行时检查协议的实例。但是,
issubclass()
不支持具有非方法成员的协议,当它们被标记为
@runtime_checkable
.

如果从协议定义中删除

@runtime_checkable
装饰器,
issubclass()
调用应该按预期工作:

from typing import Any, Protocol

class SupportsComparison(Protocol):
    def __eq__(self, other: Any) -> bool:
        pass

print(issubclass(int, SupportsComparison))  # Output: False
print(issubclass(str, SupportsComparison))  # Output: True

在这个例子中,我们删除了

@runtime_checkable
装饰器并定义了一个SupportsComparison协议,只有一个方法
__eq__()
。然后我们调用
issubclass()
来检查 int 和 str 类型是否是
SupportsComparison
协议的子类。

如果你想检查一个给定的对象是否支持你的协议,你可以使用

isinstance()
函数而不是
issubclass()
。这是一个例子:

def is_supports_comparison(obj: Any) -> bool:
    return isinstance(obj, SupportsComparison)

print(is_supports_comparison(42))  # Output: False
print(is_supports_comparison("hello"))  # Output: True

在这个例子中,我们定义了一个
is_supports_comparison()
函数,该函数将一个对象作为参数,如果该对象支持SupportsComparison协议,则返回True。我们用一个 int 和一个 str 调用这个函数来测试它们是否支持该协议。

如果您需要在您的协议中保留

@runtime_checkable
装饰器,您可以使用
isinstance()
函数而不是
issubclass()
来检查对象是否支持您的协议。
isinstance()
函数支持带有非方法成员的协议,当它们被标记为
@runtime_checkable
.

举个例子:

from typing import Any, Protocol, runtime_checkable

@runtime_checkable
class SupportsComparison(Protocol):
    def __eq__(self, other: Any) -> bool:
        pass

class MyClass:
    pass

class MyOtherClass:
    def __eq__(self, other: Any) -> bool:
        return True

# Check if an object supports SupportsComparison protocol
obj1 = MyClass()
obj2 = MyOtherClass()
print(isinstance(obj1, SupportsComparison))  # Output: False
print(isinstance(obj2, SupportsComparison))  # Output: True

在这个例子中,我们定义了带有

SupportsComparison
装饰器和单一方法
@runtime_checkable
__eq__()
协议。我们还定义了两个类
MyClass
MyOtherClass
。只有
MyOtherClass
实现了
__eq__()
方法,所以支持
SupportsComparison
协议。

要检查对象是否支持 SupportsComparison 协议,我们使用

isinstance()
函数并将对象作为第一个参数传递,将协议对象作为第二个参数传递。在此示例中,
obj1
不支持该协议,而
obj2
支持。

© www.soinside.com 2019 - 2024. All rights reserved.