在Python中,如何知道对象是否可以比较?

In Python, how to know whether objects can be compared?

abstract base classes, Python provides a way to know the behavior of objects without actually trying it out. In the standard library, we have some ABCs defined for containers in collections.abc。例如,可以测试一个参数是否可迭代:

from collections.abc import Iterable
def function(argument):
    if not isinstance(argument, Iterable):
        raise TypeError('argument must be iterable.')
    # do stuff with the argument

我希望有一个这样的 ABC 来决定是否可以比较 class 的实例,但找不到。测试 __lt__ 方法是否存在是不够的。例如,字典无法比较,但仍然定义了 __lt__(实际上与 object 相同)。

>>> d1, d2 = {}, {}
>>> d1 < d2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: dict() < dict()
>>> hasattr(d1, '__lt__')
True

所以我的问题是:有没有一种简单的方法可以做到这一点而无需自己进行比较并捕获 TypeError

我的用例类似于排序容器:我想在插入第一个元素时引发异常,而不是等待第二个元素。我考虑过将元素与自身进行比较,但有没有更好的方法:

def insert(self, element):
    try:
        element < element
    except TypeError as e:
        raise TypeError('element must be comparable.')
    # do stuff

不,没有这样的 ABC,因为 ABC 只说明那里有什么属性。 ABC 无法测试实现的性质(或者即使这些属性实际上是方法)。

存在比较方法(__lt____gt____le____ge____eq__ 指示 class 将与其他所有内容相媲美。 通常只能比较同类型或class类型的对象;数字以数字为例。

因此,大多数类型*实现比较方法,但returnNotImplemented哨兵对象当与其他不兼容的类型进行比较。将 NotImplemented 信号返回给 Python 也让右边的值在这个问题上有发言权。如果 a.__lt__(b) returns NotImplemented 那么 b.__gt__(a) 也被测试了。

基础 object 提供方法的默认实现,returning NotImplemented:

>>> class Foo:
...     pass
... 
>>> Foo() < Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: Foo() < Foo()
>>> Foo().__lt__
<method-wrapper '__lt__' of Foo object at 0x10f1cf860>
>>> Foo().__lt__(Foo())
NotImplemented

这正是 dict.__lt__ 所做的:

>>> {}.__lt__({})
NotImplemented

数字,但是,只有 return NotImplemented 当其他类型不可比较时:

>>> (1).__lt__(2)
True
>>> (1).__lt__('2')
NotImplemented
>>> 1 < 2
True
>>> 1 < '2'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < str()

因此,您的最佳选择是简单地捕获当值不可比较时抛出的TypeError


* 我不知道 Python 3 标准库中的任何类型目前没有实现比较方法。