Чому вираз 0 <0 == 0 повертає Хибність у Python?


136

Переглядаючи 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

Якщо maxsize0, черга ніколи не заповнюється.

Моє запитання - як це працює в цій справі? Як 0 < 0 == 0вважається помилковим?

>>> 0 < 0 == 0
False
>>> (0) < (0 == 0)
True
>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True

Чи дорівнює 0 <Істинно рівним False у python?
Маріно Шіміч

3
@Marino Šimić: З другого прикладу, показаного у питанні про ОП >>> (0) < (0 == 0), це явно не так.
мартино

3
Однією з причин ви не повинні писати код, як n = 0 < self.maxsize == self._qsize()в першу чергу, будь-якою мовою. Якщо вашим очам доводиться стрибати туди-сюди по лінії, щоб зрозуміти, що відбувається, це не добре написана лінія. Просто розділіть його на кілька рядків.
BlueRaja - Danny Pflughoeft

2
@Blue: Я погоджуюся з тим, щоб не писати подібне порівняння таким чином, але розділити його на окремі рядки буде трохи за бортом для двох порівнянь. Я сподіваюся, ви маєте на увазі, розділіть це на окремі порівняння. ;)
Джефф Меркадо

2
@Blue: Я не писав цього, це в Python 2.6. Я просто намагався зрозуміти, що відбувається.
Марсело Сантос

Відповіді:


113

Я вважаю, що в Python є спеціальна обробка справ для послідовностей реляційних операторів, щоб зробити порівняння діапазонів легко виразити. Набагато приємніше вміти сказати, 0 < x <= 5ніж сказати (0 < x) and (x <= 5).

Вони називаються ланцюговими порівняннями . І це посилання на документацію для них.

У інших випадках, про які ви говорите, дужки змушують застосувати один реляційний оператор перед іншим, і тому вони більше не є ланцюговими порівняннями. А так Trueі Falseмають значення як цілі числа ви отримаєте відповіді , які ви робите з версій дужок.


цікаво спробувати деякі з цих порівнянь та вказати int () та bool (). Я зрозумів, що bool () будь-якого ненульового значення - 1. Звичайно, я ніколи б навіть не намагався безпосередньо вказати щось, крім bool (0) або bool (1) перед цим мислительним експериментом
j_syk

Чи мали намір замість цього посилання на цей розділ? docs.python.org/2/reference/expressions.html#comparisons
tavnab

@tavnab - Так. Я спробую запам'ятати, щоб це виправити. Я також збираюся перевірити історію редагування. Це не здається помилкою, яку я зробив би. 😕
всезначний

42

Тому що

(0 < 0) and (0 == 0)

є False. Ви можете зв'язати між собою оператори порівняння, і вони автоматично розширюються на попарні порівняння.


EDIT - роз'яснення щодо True та False в Python

У Python Trueі Falseє лише екземпляри bool, що є підкласом int. Іншими словами, Trueнасправді це лише 1.

Сенс цього в тому, що ви можете використовувати результат булевого порівняння точно як ціле число. Це призводить до плутанини подібних речей

>>> (1==1)+(1==1)
2
>>> (2<1)<1
True

Але це відбудеться лише в тому випадку, якщо ви скористаєтеся дужками порівнянь, щоб вони оцінювалися спочатку. Інакше Python розширить оператори порівняння.


2
Вчора я побачив цікаве використання для булевих значень, що використовуються як цілі числа. Вираз 'success' if result_code == 0 else 'failure'можна переписати, оскільки ('error', 'success')[result_code == 0]до цього я ніколи не бачив булевого символу, використовуваного для вибору елемента в списку / кортежі.
Ендрю Кларк

'bool' був доданий десь біля Python 2.2.
MRAB

18

Дивна поведінка, яку ви відчуваєте, походить від здатності пітонів до ланцюгових умов. Оскільки він знаходить 0 не менше 0, він визначає, що весь вираз оцінюється як хибний. Як тільки ви розділите це на окремі умови, ви змінюєте функціональність. Спочатку це по суті є тестуванням цього a < b && b == cдля вашого оригінального твердження від a < b == c.

Ще один приклад:

>>> 1 < 5 < 3
False

>>> (1 < 5) < 3
True

1
OMG, a < b && b == cте саме, що a < b == cOO
Кирил Кіров

9
>>> 0 < 0 == 0
False

Це ланцюгове порівняння. Він повертає істину, якщо кожне попарне порівняння в свою чергу є істинним. Це еквівалент(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.


7

Дивлячись на розбирання (байти коди) , то зрозуміло , чому 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в стек пітона.

Тепер зауважте рядок 11: JUMP_IF_FALSE_OR_POP 23 Це означає, що якщо 0 < 0повернення Falseвиконують перехід до рядка 23.

Тепер, 0 < 0це False, так що стрибок береться, який виходить з стека з Falseякої є повертається значенням для всього висловлювання 0 < 0 == 0, навіть якщо == 0частина навіть не перевіряється.

Отже, підсумовуючи, відповідь, як сказано в інших відповідях на це питання. 0 < 0 == 0має особливе значення. Компілятор оцінює це двома членами: 0 < 0і 0 == 0. Як і у будь-яких складних булевих виразів andміж ними, якщо перший не вдається, то другий навіть не перевіряється.

Сподіваюся, що це трохи просвітить справи, і я дуже сподіваюся, що метод, який я використовував для аналізу цієї несподіваної поведінки, спонукатиме інших намагатися те саме в майбутньому.


Чи не було б просто простіше опрацювати це з специфікації, а не зворотною інженерією однієї конкретної реалізації?
Девід Геффернан

Ні. Це коротка відповідь. Я вважаю, що це залежить від вашої особистості. Якщо ви ставитесь до перегляду "чорного поля" і віддаєте перевагу отримувати відповіді з специфікацій та документації, то ця відповідь вас лише збентежить. Якщо ви любите копатися та розкривати внутрішні речі, то ця відповідь саме для вас. Ваше зауваження, що ця зворотна інженерія стосується лише однієї конкретної реалізації, є правильною і її слід зазначити, але це не було суть цієї відповіді. Це демонстрація того, як легко в пітоні поглянути "під капот" для тих, хто достатньо цікавий.
сб

1
Проблема із зворотною інженерією - її відсутність прогнозованої потужності. Це не спосіб вивчити нову мову.
Девід Геффернан

Ще одна річ, яку я мушу додати, - це те, що технічні характеристики та документації не завжди є повними і в більшості випадків не дають відповідей на такі конкретні випадки. Тоді, я вважаю, не слід боятися досліджувати, досліджувати та досягати глибшого, наскільки потрібно, щоб отримати відповіді.
сб

2

Як згадується іншим, 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.


2

можливо, цей уривок із Документів може допомогти:

Це так звані методи "багатого порівняння", і вони називаються операторами порівняння, надаючи перевагу __cmp__()нижче. Відповідність між символами операторів і іменами методів виглядають наступним чином : x<yдзвінки 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).

Багатий метод порівняння може повернути синглтон, NotImplementedякщо він не реалізує операцію для заданої пари аргументів. За умовою Falseі Trueповертаються для успішного порівняння. Однак ці методи можуть повернути будь-яке значення, тому якщо оператор порівняння використовується в булевому контексті (наприклад, за умови заяви if), Python закликає bool()значення, щоб визначити, чи є результат істинним чи помилковим.

Між операторами порівняння не маються на увазі зв’язки. Істина x==yне означає, що x!=y це помилково. Відповідно, визначаючи __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, за винятком того, що кожен вираз оцінюється не більше одного разу.


1

Ось він у всій красі.

>>> 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
>>> 

0

Я думаю, що Python робить це дивно між магією. Те саме, що 1 < 2 < 3засіб 2 становить від 1 до 3.

У цьому випадку я думаю, що це робить [середній 0] більше [лівий 0] і дорівнює [правому 0]. Середній 0 не перевищує лівий 0, тому він оцінюється як хибний.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.