自定义链式比较

时间:2016-05-10 14:07:17

标签: python

Python允许像x > y > z这样的表达式,根据文档,它等同于(x > y) and (y > z),除了y只评估一次。 (https://docs.python.org/3/reference/expressions.html

但是,如果我自定义比较功能,这似乎会中断。例如。假设我有以下类:(对于大块的道歉,但是一旦你阅读__eq__方法,其余的都是微不足道的。)

class CompareList(list):
    def __repr__(self):
        return "CompareList([" + ",".join(str(x) for x in self) + "])"

    def __eq__(self, other):
        if isinstance(other, list):
            return CompareList(self[idx] == other[idx] for idx in xrange(len(self)))
        else:
            return CompareList(x == other for x in self)

    def __ne__(self, other):
        if isinstance(other, list):
            return CompareList(self[idx] != other[idx] for idx in xrange(len(self)))
        else:
            return CompareList(x != other for x in self)

    def __gt__(self, other):
        if isinstance(other, list):
            return CompareList(self[idx] > other[idx] for idx in xrange(len(self)))
        else:
            return CompareList(x > other for x in self)

    def __ge__(self, other):
        if isinstance(other, list):
            return CompareList(self[idx] >= other[idx] for idx in xrange(len(self)))
        else:
            return CompareList(x >= other for x in self)

    def __lt__(self, other):
        if isinstance(other, list):
            return CompareList(self[idx] < other[idx] for idx in xrange(len(self)))
        else:
            return CompareList(x < other for x in self)

    def __le__(self, other):
        if isinstance(other, list):
            return CompareList(self[idx] <= other[idx] for idx in xrange(len(self)))
        else:
            return CompareList(x <= other for x in self)

现在我可以做一些有趣的事情,比如CompareList([10, 5]) > CompareList([5, 10]),它会正确返回CompareList([True,False])

然而,链接这些操作并不能很好地运作:

low = CompareList([1])
high = CompareList([2])
print(low > high > low) # returns CompareList([True])

为什么不呢?引擎盖下发生了什么?我知道它不等于(low > high) > low = (False > low)(因为那将返回False)。它可能是low > (high > low),但就运算符优先级(通常是从左到右)而言没有意义。

3 个答案:

答案 0 :(得分:8)

  

Python允许像x > y > z这样的表达式,根据文档,这些表达式等同于(x > y) and (y > z),除了y只评估一次。

根据这一点,low > high > low将等同于(low > high) and (high > low)

>>> x = low > high   # CompareList([False])
>>> y = high > low   # CompareList([True]) 
>>> x and y
CompareList([True])

有关x and y的文档中的更多内容:

  

x and y:如果x为假,则为x,否则为y

在上述情况中:

>>> x is False
False
>>> x if x is False else y     # x and y
CompareList([True])

所以当你x and y时,它会返回y CompareList([True])

答案 1 :(得分:3)

其他答案是正确的,但我想解决这个问题的实际缺乏实现,因为,正如我所相信的那样,OP希望从{{1是low > high > low

确实,CompareList([False])评估为low > high > low,而(low > high) and (high > low)评估为CompareList([False]) is False(这意味着它是False),那么第二个操作数True运算符被评估并返回(因为它也计算为and)。

实现链式比较的关键是覆盖Trueand上的__gt__布尔运算符。

不幸的是,没有办法做到这一点,而且可能不会成功。 PEP 335 - Overloadable Boolean Operators proposal被Guido拒绝,但他可能会考虑将链式比较作为&lt; b&lt; c overable [1]

除非那一刻,否则在使用链式比较时无法使您的示例按预期工作。

实现正确结果的唯一方法是重写__lt__方法并编写如下比较:

__and__

然后,通过下面的表格,您将得到正确答案:

def CompareList(list):
    ...
    def __and__(self, other):
        if isinstance(other, list):
            return CompareList(self[idx] and other[idx] for idx in range(len(self)))
        else:
            return CompareList(x and other for x in self)

答案 2 :(得分:1)

您应该从比较方法中返回一个布尔值。

引用documentation for "rich comparison" methods

  

按照惯例,成功返回False和True   比较。但是,这些方法可以返回任何值,所以如果   比较运算符用于布尔上下文中(例如,在   if语句的条件),Python会将值调用bool()   确定结果是真还是假。

为了解决这个问题:

exp1 = low > high
print(exp1)
print(bool(exp1))
exp2 = high > low
print(exp2)
print(bool(exp2))

会给你

CompareList([False])
True
CompareList([True])
True

现在我们进行最后一次操作并打印出结果

print(exp1 and exp2)

因为这两个值都会计算为True,所以

CompareList([True])