Индивидуальные сочетания
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])