Каков приоритет оператора при написании двойного неравенства в 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
заявление.
Я спрашиваю, существует ли аналогичная распаковка кода из связанного неравенства, с помощью которого я могу перехватить то, что он делает, и перенаправить его на использование логических операторов в стиле массива вместо тех классов, которые мне нужны.
Я чувствую, что это очень ясно из моего вопроса, особенно из примера, но, надеюсь, это проясняет ситуацию.
4 ответа
Я не совсем уверен, что вы ищете, но быстрая разборка показывает, что 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 л
По соглашению False и True возвращаются для успешного сравнения. Однако эти методы могут возвращать любое значение...
Оба имеют одинаковый приоритет, но оцениваются слева направо согласно документации. Выражение формы a <= b <= c
расширяется до a <= b and b <= c
,
но реальный вопрос в том, как это реализуется в коде.
Вы имеете в виду, как переводчик это преобразовывает, или как? Ты уже сказал
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 каждого сравнения в расширенном выражении).
Я хотел ответить по поводу исходного примера:
Почему:(1 < 3) < 2 == True
Пока:1 < 3 < 2 == False
Итак, давайте разберемся, второе (очевидное) первое:
(1 < 3) and (3 < 2)
упрощается до(True) and (False)
которыйFalse
Далее, менее очевидный:
(1 < 3) < 2
что упрощается до(True) < 2
что упрощается до1 < 2
, которыйTrue
.
Вот еще один ответ, который объясняет, что это связано с тем, что логические значения являются подтипом целых чисел: /questions/20083183/pochemu-python-schitaet-chto-logicheskoe-znachenie-yavlyaetsya-tselyim-chislom/20083184#20083184
Вот также официальная документация по логическому целочисленному типу: https://docs.python.org/3/c-api/bool.html?highlight=boolean%20int