Pythonで式0 <0 == 0がFalseを返すのはなぜですか?


136

Python 2.6のQueue.pyを調べたところ、この構造が少し変であることがわかりました。

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偽りと見なされますか?

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

Pythonで0 <TrueはFalseに等しいですか?
MarinoŠimić11年

3
@MarinoŠimić:OPの質問に示されている2番目の例から>>> (0) < (0 == 0)、それは明らかにそうではありません。
martineau

3
n = 0 < self.maxsize == self._qsize()そもそも、どんな言語でもコードを書くべきではない理由の1つです。何が起こっているのかを理解するために、目を線の前後に何度か投げる必要がある場合、それはうまく書かれた線ではありません。数行に分割するだけです。
BlueRaja-Danny Pflughoeft、

2
@Blue:そのような比較をそのように記述しないことに同意しますが、それを別々の行に分割することは、2つの比較では少し行き過ぎです。つまり、別の比較に分割してください。;)
ジェフメルカド

2
@Blue:書きませんでした。Python2.6です。私は何が起こっているのかを理解しようとしていました。
Marcelo Santos、

回答:


113

Pythonには、範囲比較を表現しやすくするために、関係演算子のシーケンスに対する特別なケース処理があると思います。と言う0 < x <= 5よりも言うことができる方がずっといい(0 < x) and (x <= 5)です。

これらは連鎖比較と呼ばます。そして、それは彼らのためのドキュメントへのリンクです。

あなたが話している他のケースでは、括弧は一方の関係演算子が他方の前に適用されることを強制するので、それらはもはや連鎖比較ではありません。そして、整数として値TrueFalse持っているので、括弧で囲まれたバージョンから行う答えを得ることができます。


これらの比較をいくつか試し、int()とbool()を指定するのは興味深いことです。私は、任意の非ゼロのブール値は、()私も直接、この思考実験の前にブール値(0)またはBOOL(1)以外を指定しようとしたてもみなかった1推測であることに気づいた
j_syk

代わりにこのセクションにリンクするつもりでしたか?docs.python.org/2/reference/expressions.html#comparisons
tavnab

@tavnab-はい。私はそれを修正することを忘れないようにします。編集履歴も確認します。それは私が犯す間違いではないようです。😕
Omnifarious

42

なぜなら

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

ですFalse。比較演算子をチェーンすることができ、それらは自動的にペアワイズ比較に展開されます。


編集-PythonのTrueとFalseに関する説明

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]これまでは、リスト/タプル内の項目を選択するために使用されるブール値を見たことはありませんでした。
Andrew Clark、

'bool'はPython 2.2の頃に追加されました。
MRAB、

18

あなたが経験している奇妙な振る舞いは、条件を連鎖するpythonの能力に由来します。0が0以上であることを検出したため、式全体の評価がfalseであると判断します。これを別々の条件に分解するとすぐに、機能を変更します。最初は本質的a < b && b == cに、の元のステートメントに対するテストですa < b == c

もう一つの例:

>>> 1 < 5 < 3
False

>>> (1 < 5) < 3
True

1
OMG a < b && b == ca < b == cOO と同じです
キリルキーロフ'20年

9
>>> 0 < 0 == 0
False

これは連鎖比較です。次に、各ペアごとの比較がtrueの場合、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に評価。


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 < 0False、Pythonスタックに明らかに戻るかどうかを確認します。

11行目に注目してください。JUMP_IF_FALSE_OR_POP 23 これは、0 < 0リターンFalseが23行にジャンプすることを意味します。

これで0 < 0is Falseなので、ジャンプが行われ、パーツがチェックされていなくても、False式全体の戻り値であるa がスタックに残ります。0 < 0 == 0== 0

結論として、答えはこの質問の他の答えで述べられているようなものです。 0 < 0 == 0特別な意味があります。コンパイラーはこれを2つの項に評価します:0 < 0および0 == 0andそれらの間にある任意の複雑なブール式と同様に、最初の式が失敗すると、2番目の式はチェックされません。

これにより状況が少し明らかになることを願っています。私がこの予期しない動作を分析するために使用した方法が、他の人が将来同じことを試すことを奨励することを願っています。


特定の実装をリバースエンジニアリングするよりも、仕様に基づいて計算する方が簡単ではないでしょうか。
デビッドヘファーナン2014

いいえ、それは短い答えです。それはあなたの性格次第だと思います。「ブラックボックス」ビューに関連していて、仕様やドキュメントから回答を得たい場合、この回答は混乱を招くだけです。ものの内部を掘り下げて明らかにしたい場合、この答えはあなたのためです。このリバースエンジニアリングは1つの特定の実装にのみ関連しているという指摘は正しく、指摘する必要がありますが、それはこの回答の目的ではありませんでした。十分に興味がある人のために、「フードの下で」一目でPythonを見ることがいかに簡単かを示すデモです。
SatA 2014

1
リバースエンジニアリングの問題は、予測能力の欠如です。新しい言語を学ぶ方法ではありません。
デビッドヘファーナン2014

追加しなければならないもう1つのことは、仕様とドキュメントが常に完全であるとは限らず、ほとんどの場合、そのような特定のケースに対する回答を提供しないことです。それから、答えを得るために必要なだけ探求し、調査し、より深く到達することを恐れてはなりません。
SatA 2014

2

他の人が言及したように、yは1回しか評価されないというボーナスx comparison_operator y comparison_operator zがある構文上の砂糖です(x comparison_operator y) and (y comparison_operator z)

だからあなたの表現は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()値を呼び出して結果がtrueかfalseかを判断します。

比較演算子間に暗黙の関係はありません。の真実は、x==yそれx!=y が偽であることを意味しません。したがって、を定義するとき は、演算子が期待どおりに動作__eq__()する__ne__()ように定義する必要もあります。__hash__()カスタム比較操作をサポートし、辞書キーとして使用可能なハッシュ可能なオブジェクトを作成する際の重要な注意事項については、上の段落を参照してください。

これらのメソッドのスワップされた引数のバージョンはありません(左側の引数が操作をサポートしていないが、右側の引数はサポートしている場合に使用されます)。むしろ、__lt__()そして__gt__() 互いの反映で、__le__() かつ__ge__()互いの反射であり、__eq__()そして__ne__() 自分自身の反射です。

豊富な比較メソッドへの引数は強制されません。

これらは比較でしたが、比較チェーンしているので、次のことを知っておく必要があります

比較は任意に連鎖できます。たとえば、yは1回しか評価されない(ただし、x <yがfalseであることが判明した場合、zはまったく評価されない)以外x < y <= zは、と同等x < y and y <= zです。

正式には、a、b、c、...、y、zが式で、op1、op2、...、opNが比較演算子の場合、a op1 b op2 c ... y opN zはa op1 bと同等です。およびb op2 cおよび... y opN z。ただし、各式は最大で1回だけ評価されます。


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 < 32が1と3の間であることを意味するのと同じです。

この場合、[中央の0]は[左の0]より大きく、[右の0]に等しいと考えています。中央の0は左の0より大きくないため、falseと評価されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.