Индивидуальные сочетания

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), но это не имеет смысла с точки зрения приоритета оператора (обычно слева направо).

Ответы

Ответ 1

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 и y:

x and y: if x false, тогда x, else y

В приведенном выше случае:

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

поэтому, когда вы делаете x and y, он возвращает y, который равен CompareList([True]).

Ответ 2

Другие ответы правильные, но я хотел бы решить фактическую <забастовку > отсутствие реализации для этой проблемы, потому что, как я считаю, что OP хотел бы получить в результате от low > high > low является CompareList([False]).

Действительно, low > high > low оценивается как (low > high) and (high > low), и поскольку CompareList([False]) is False оценивается как False (что означает, что это True), тогда второй операнд оператора and получает оценку и возвращается (как он также оценивает значение True).

Ключом к реализации прикованного сравнения является переопределение булевого оператора and вдоль __gt__ и __lt__.

К сожалению, нет никакого способа сделать это, и, вероятно, этого не произойдет. Предложение PEP 335 - Overloadable Boolean Operators было отклонено Гвидо, но он мог бы подумать о том, чтобы сделать скованные сравнения, такие как < b < c перегружаемый [1].

Если этот момент не существует, вы не сможете заставить ваш пример работать как ожидалось при использовании сопоставленных цепочек.

Единственный способ добиться правильного результата - переопределить метод __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)

Затем, написав в форме ниже, вы получите правильный ответ:

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

Ответ 3

Вы должны вернуть логическое значение из методов сравнения.

Чтобы привести документацию для методов "богатого сравнения" :

По соглашению, 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])