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

时间:2015-04-05 12:11:40

标签: python python-3.x comparison-operators abstract-base-class

使用abstract base classes,Python提供了一种了解对象行为的方法,而无需实际尝试。在标准库中,我们为collections.abc中的容器定义了一些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来决定是否可以比较一个类的实例但是找不到它。测试__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

1 个答案:

答案 0 :(得分:4)

不,没有这样的ABC,因为ABC 只规定了哪些属性。 ABCs无法测试实现的性质(或者即使这些属性实际上是方法)。

比较方法(__lt____gt____le____ge____eq__)的存在确实指示这个班级将与其他一切相媲美。 通常你只能比较相同类型或类别的对象;以数字为例的数字。

因此,大多数类型 * 实现比较方法,但在与其他不兼容类型进行比较时返回NotImplemented sentinel对象。将NotImplemented信号返回给Python,以便在这个问题上给出右手值。如果a.__lt__(b)返回NotImplemented,则b.__gt__(a)也会被测试。

基础object提供方法的默认实现,返回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

但是,当另一种类型无法比较时,数字只返回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标准库中目前没有实现比较方法的任何类型。