Каков приоритет оператора при написании двойного неравенства в 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 каждого сравнения в расширенном выражении).