Почему выражение 0 <0 == 0 возвращает False в Python?
Заглянув в Queue.py в Python 2.6, я нашел эту конструкцию, которая мне показалась немного странной:
def full(self):
"""Return True if the queue is full, False otherwise
(not reliable!)."""
self.mutex.acquire()
n = 0 < self.maxsize == self._qsize()
self.mutex.release()
return n
Если maxsize
равно 0, очередь не заполняется.
Мой вопрос в том, как он работает в этом случае? Как 0 < 0 == 0
считается False?
>>> 0 < 0 == 0
False
>>> (0) < (0 == 0)
True
>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True
Ответы
Ответ 1
Я считаю, что у Python есть специальная обработка сообщений для последовательностей реляционных операторов, чтобы упростить их сравнение. Гораздо приятнее сказать 0 < x <= 5
, чем сказать (0 < x) and (x <= 5)
.
Они называются скованные сравнения. И это ссылка на документацию для них.
В других случаях, о которых вы говорите, скобка заставляет использовать один реляционный оператор перед другим, и поэтому они больше не связаны друг с другом. И поскольку True
и False
имеют значения как целые числа, вы получаете ответы, которые вы делаете из версий в скобках.
Ответ 2
Поскольку
(0 < 0) and (0 == 0)
есть False
. Вы можете связать вместе операторы сравнения, и они автоматически расширяются в попарные сравнения.
РЕДАКТИРОВАНИЕ - разъяснение прав и прав в Python
В Python True
и False
есть только экземпляры bool
, который является подклассом int
. Другими словами, True
действительно всего 1.
Точка в том, что вы можете использовать результат булевого сравнения точно так же, как целое число. Это приводит к запутыванию таких вещей, как
>>> (1==1)+(1==1)
2
>>> (2<1)<1
True
Но это произойдет, только если вы скопируете сравнения, чтобы они сначала оценивались. В противном случае Python расширит операторы сравнения.
Ответ 3
Странное поведение вашего опыта происходит от способности питонов к условиям цепи. Поскольку он находит, что 0 не меньше 0, он решает, что все выражение принимает значение false. Как только вы разбиваете это на отдельные условия, вы меняете функциональность. Первоначально он в основном тестирует, что a < b && b == c
для вашего исходного утверждения a < b == c
.
Другой пример:
>>> 1 < 5 < 3
False
>>> (1 < 5) < 3
True
Ответ 4
>>> 0 < 0 == 0
False
Это скованное сравнение. Он возвращает true, если каждое попарное сравнение по очереди истинно. Это эквивалентно (0 < 0) and (0 == 0)
>>> (0) < (0 == 0)
True
Это эквивалентно 0 < True
, который оценивается как True.
>>> (0 < 0) == 0
True
Это эквивалентно False == 0
, который оценивается как True.
>>> 0 < (0 == 0)
True
Эквивалент 0 < True
, который, как указано выше, имеет значение True.
Ответ 5
Глядя на разборку (коды байтов), очевидно, что 0 < 0 == 0
есть False
.
Вот анализ этого выражения:
>>>import dis
>>>def f():
... 0 < 0 == 0
>>>dis.dis(f)
2 0 LOAD_CONST 1 (0)
3 LOAD_CONST 1 (0)
6 DUP_TOP
7 ROT_THREE
8 COMPARE_OP 0 (<)
11 JUMP_IF_FALSE_OR_POP 23
14 LOAD_CONST 1 (0)
17 COMPARE_OP 2 (==)
20 JUMP_FORWARD 2 (to 25)
>> 23 ROT_TWO
24 POP_TOP
>> 25 POP_TOP
26 LOAD_CONST 0 (None)
29 RETURN_VALUE
Отметьте строки 0-8: эти строки проверяют, есть ли 0 < 0
, который явно возвращает False
в стек python.
Теперь обратите внимание на строку 11: JUMP_IF_FALSE_OR_POP 23
Это означает, что если 0 < 0
возвращает False
выполнить переход к строке 23.
Теперь 0 < 0
является False
, поэтому выполняется переход, который оставляет стек с False
, который является возвращаемым значением для всего выражения 0 < 0 == 0
, хотя часть == 0
isn ' t даже проверено.
Итак, в заключение, ответ подобен сказанному в других ответах на этот вопрос.
0 < 0 == 0
имеет особое значение. Компилятор оценивает это на два термина: 0 < 0
и 0 == 0
. Как и с любыми сложными булевыми выражениями с and
между ними, если первое не удается, второе не проверяется даже.
Надеется, что это немного просветит, и я очень надеюсь, что метод, который я использовал для анализа этого неожиданного поведения, побудит других попробовать то же самое в будущем.
Ответ 6
Возможно, эта выдержка из docs может помочь:
Это так называемые "богатые сравнения" и называются для предпочтительных операторов сравнения ниже __cmp__()
. Переписка между символами оператора и методом имена следующие: x<y
calls x.__lt__(y)
, x<=y
вызывает x.__le__(y)
, x==y
вызывает x.__eq__(y)
, x!=y
и x<>y
вызов x.__ne__(y)
, x>y
x.__gt__(y)
и x>=y
x.__ge__(y)
.
Богатый метод сравнения может вернуться singleton NotImplemented
, если он не выполняет операцию для заданная пара аргументов. От соглашения, False
и True
являются вернулся для успешного сравнения. Однако эти методы могут возвращать любые значение, поэтому, если оператор сравнения используется в булевом контексте (например, в условие оператора if), Python вызовет bool()
для значения определить, является ли результат истинным или ложь.
Нет подразумеваемых отношений среди операторов сравнения. истина x==y
не означает, что x!=y
false. Соответственно, при определении __eq__()
, следует также определить __ne__()
, чтобы операторы действовали должным образом. См. Пункт на __hash__()
для некоторых важных заметок по созданию хешируемых объектов, которые поддержка пользовательских операций сравнения и могут использоваться в качестве словарных клавиш.
Нет версий с замененным аргументом из этих методов (используется, когда левый аргумент не поддерживает но правильный аргумент делает); скорее, __lt__()
и __gt__()
отражают друг друга, __le__()
и __ge__()
являются друг другом отражение и __eq__()
и __ne__()
являются их собственным отражением.
Аргументы для богатых методов сравнения никогда не принуждаются.
Это были сравнения, но поскольку вы цепочки сравнения, вы должны знать, что:
Сравнение может быть прикованным произвольно, например, x < y <= z
является эквивалентно x < y and y <= z
, кроме что y оценивается только один раз (но в оба случая z вообще не оцениваются когда x < y считается ложным).
Формально, если a, b, c,..., y, z являются выражения и op1, op2,..., opN являются операторы сравнения, то op1 b op2 c... y opN z эквивалентно op1 b и b op2 c и... y opN z, кроме что каждое выражение оценивается в самый раз.
Ответ 7
Как упоминалось выше, x comparison_operator y comparison_operator z
является синтаксическим сахаром для (x comparison_operator y) and (y comparison_operator z)
с бонусом, что y оценивается только один раз.
Итак, ваше выражение 0 < 0 == 0
действительно (0 < 0) and (0 == 0)
, которое оценивается как False and True
, которое просто False
.
Ответ 8
Вот он во всей красе.
>>> class showme(object):
... def __init__(self, name, value):
... self.name, self.value = name, value
... def __repr__(self):
... return "<showme %s:%s>" % (self.name, self.value)
... def __cmp__(self, other):
... print "cmp(%r, %r)" % (self, other)
... if type(other) == showme:
... return cmp(self.value, other.value)
... else:
... return cmp(self.value, other)
...
>>> showme(1,0) < showme(2,0) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
False
>>> (showme(1,0) < showme(2,0)) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
cmp(<showme 3:0>, False)
True
>>> showme(1,0) < (showme(2,0) == showme(3,0))
cmp(<showme 2:0>, <showme 3:0>)
cmp(<showme 1:0>, True)
True
>>>
Ответ 9
Я думаю, что Python делает это странно между магией. То же, что и 1 < 2 < 3
означает, что 2 находится между 1 и 3.
В этом случае, я думаю, что выполнение [middle 0] больше, чем [left 0] и равно [right 0]. Среднее 0 не больше, чем левое 0, поэтому оно принимает значение false.