Каков приоритет оператора при написании двойного неравенства в Python (явно в коде и как это можно переопределить для массивов?)

Каков конкретный код, чтобы он исполнялся, когда я просил что-то вроде

>>> 1 <= 3 >= 2
True

Если оба имеют одинаковый приоритет и это просто порядок их оценки, то почему второе неравенство функционирует как (3 >= 2) вместо (True >= 2)

Рассмотрим, например, разницу между этими

>>> (1 < 3) < 2
True

>>> 1 < 3 < 2
False

Это просто чистая синтаксическая короткая резка, закодированная в Python, для расширения второй как and двух операторов?

Могу ли я изменить это поведение для класса, чтобы a <= b <= c расширялся до чего-то другого? Это выглядит так:

a (logical operator) b (logical operator) c 
    --> (a logical operator b) and (b logical operator c)

но реальный вопрос заключается в том, как это реализуется в коде.

Мне любопытно, чтобы я мог реплицировать поведение типа __lt__ и __gt__ в некоторых из моих собственных классов, но я смущен тем, как это выполняется, поддерживая константу среднего аргумента.

Вот пример:

>>> import numpy as np

>>> tst = np.asarray([1,2,3,4,5,6])

>>> 3 <= tst
array([False, False,  True,  True,  True,  True], dtype=bool)

>>> 3 <= tst <= 5
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/home/ely/<ipython-input-135-ac909818f2b1> in <module>()
----> 1 3 <= tst <= 5

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Было бы неплохо переопределить это, чтобы он "просто работал" с массивами, например:

>>> np.logical_and(3 <= tst, tst <= 5)
array([False, False,  True,  True,  True,  False], dtype=bool)

Добавлен для уточнения

В комментариях указано, что я плохо объяснил этот вопрос. Вот некоторые поясняющие замечания:

1) Я не ищу простого объяснения того факта, что интерпретатор выскакивает and между двумя цепными неравенствами. Я уже знал это и сказал это выше.

2) По аналогии с тем, что я хочу сделать, рассмотрим оператор with (ссылка). Следующее:

with MyClass(some_obj) as foo:
    do_stuff()

распаковывается в

foo = MyClass(some_obj)
foo.__enter__()
try:
    do_stuff()
finally:
    foo.__exit__()

Итак, написав MyClass соответствующим образом, я могу делать много специальных вещей внутри оператора with.

Я спрашиваю, есть ли аналогичная распаковка кода прикованного неравенства, с помощью которого я могу перехватить то, что он делает, и перенаправить его на использование логических операторов в стиле массива, а не только на классы, которые меня волнуют.

Я чувствую, что это очень ясно из моего вопроса, особенно пример, но, надеюсь, это делает его более понятным.

Ответ 1

Я не совсем уверен, что вы ищете, но быстрый разбор показывает, что a < b < c не скомпилирован в тот же байт-код, что и a < b and b < c

>>> import dis
>>>
>>> def f(a, b, c):
...     return a < b < c
...
>>> dis.dis(f)
  2           0 LOAD_FAST                0 (a)
              3 LOAD_FAST                1 (b)
              6 DUP_TOP
              7 ROT_THREE
              8 COMPARE_OP               0 (<)
             11 JUMP_IF_FALSE_OR_POP    21
             14 LOAD_FAST                2 (c)
             17 COMPARE_OP               0 (<)
             20 RETURN_VALUE
        >>   21 ROT_TWO
             22 POP_TOP
             23 RETURN_VALUE
>>>
>>> def f(a, b, c):
...     return a < b and b < c
...
>>> dis.dis(f)
  2           0 LOAD_FAST                0 (a)
              3 LOAD_FAST                1 (b)
              6 COMPARE_OP               0 (<)
              9 JUMP_IF_FALSE_OR_POP    21
             12 LOAD_FAST                1 (b)
             15 LOAD_FAST                2 (c)
             18 COMPARE_OP               0 (<)
        >>   21 RETURN_VALUE

Изменить 1: Копаем дальше, я думаю, что это что-то странное или неправильное с numpy. Рассмотрим этот пример кода, я думаю, он работает так, как вы ожидали.

class Object(object):
    def __init__(self, values):
        self.values = values
    def __lt__(self, other):
        return [x < other for x in self.values]
    def __gt__(self, other):
        return [x > other for x in self.values]

x = Object([1, 2, 3])
print x < 5 # [True, True, True]
print x > 5 # [False, False, False]
print 0 < x < 5 # [True, True, True]

Изменить 2: На самом деле это не работает "правильно"...

print 1 < x # [False, True, True]
print x < 3 # [True, True, False]
print 1 < x < 3 # [True, True, False]

Я думаю, что он сравнивает логические значения с числами во втором сравнении 1 < x < 3.

Изменить 3: Мне не нравится идея возвращать небулевые значения из специальных методов gt, lt, gte, lte, но на самом деле это не ограничивается в соответствии с документацией Python.

http://docs.python.org/reference/datamodel.html#object. lt

По соглашению, False и True возвращаются для успешной сравнение. Однако эти методы могут возвращать любое значение...

Ответ 2

Оба имеют одинаковый приоритет, но оцениваются слева направо в соответствии с документацией. Выражение формы a <= b <= c расширяется до a <= b and b <= c.

Ответ 3

но реальный вопрос заключается в том, как это реализуется в коде.

Вы имеете в виду, как интерпретатор преобразует его или что? Вы уже сказали

a (logical operator) b (logical operator) c 
    --> (a logical operator b) and (b logical operator c)

, поэтому я не уверен, что вы спрашиваете здесь ОК, я понял: нет, вы не можете переопределить расширение от a < b < c до (a < b) and (b < c) IIUC.


Мне любопытно, что я могу реплицировать поведение типа __lt__ и __gt__ в некоторых из моих собственных классов, но я смущен тем, как это выполняется с постоянной константы среднего.

Это зависит от того, какие из a, b и c в выражении a < b < c являются экземплярами вашего собственного класса. Внедрение ваших __lt__ и __gt__ и методов происходит частично, но в документации указано, что:

Нет версий этих методов с заменой аргументов (для использования, когда левый аргумент не поддерживает операцию, но правильный аргумент)

Итак, если вы хотите Int < MyClass < Int, вам не повезло. Вам нужно, как минимум, MyClass < MyClass < Something (поэтому экземпляр вашего класса находится в LHS каждого сравнения в расширенном выражении).