【问题标题】:In Python, how to know whether objects can be compared?在 Python 中,如何知道对象是否可以进行比较?
【发布时间】:2015-06-10 00:23:16
【问题描述】:

通过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

【问题讨论】:

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


    【解决方案1】:

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

    比较方法(__lt____gt____le____ge____eq__)的存在确实表明该类将与其他所有方法进行比较. 通常你只能比较相同类型或类型类的对象;以数字为例。

    因此,大多数类型*实现了比较方法,但在与其他不兼容类型比较时返回NotImplemented哨兵对象。将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 标准库中有没有实现比较方法的类型。

    【讨论】:

    • ABC 不仅说明有哪些属性。这就是他们可以强制执行的全部内容,但由于他们是选择加入的(需要从它继承或明确注册),大多数 ABC 可以并且确实定义了一个合同。当isinstance(x, abc.Sized) 时,假设x.__len__ 是一个返回非负整数的方法是非常合理的。当然,它不会以编程方式进行检查,但由于 Python 是动态的,因此无法真正检查任何内容。
    • @delnan:当然,这就是 ABC 的意图。但由于比较方法的约定是在传递不兼容的对象类型时返回NotImplemented,因此您无法使用 ABC 测试该约定的实现程度。
    • 没错,双重调度让它变得更加复杂。我主要反对一般性陈述“仅规定存在哪些属性”和“(或者即使这些属性实际上是方法)”。
    • 感谢您的详细回答。除了数字之外,你知道是否有很多对象可以与其他类型进行比较?
    • @Fracis:在 Python 2 中几乎所有类型都可以与其他类型相媲美;这是错误的常见来源(尤其是在涉及数字和字符串时);这样做是为了支持对异源序列进行排序。在 Python 3 中,我目前无法想到除数字之外的任何示例(其中浮点数和整数具有明确定义的顺序)。
    猜你喜欢
    • 2015-01-27
    • 1970-01-01
    • 1970-01-01
    • 2016-09-10
    • 2015-01-28
    • 1970-01-01
    • 2013-06-28
    • 1970-01-01
    • 2021-11-08
    相关资源
    最近更新 更多