Python(Python 3.6でのみチェックしましたが、以前のバージョンの多くにも当てはまると思います):
(0, 0) == 0, 0 # results in a two element Tuple: (False, 0)
0, 0 == (0, 0) # results in a two element Tuple: (0, False)
(0, 0) == (0, 0) # results in a boolean True
しかし:
a = 0, 0
b = (0, 0)
a == b # results in a boolean True
2つのアプローチで結果が異なるのはなぜですか?等価演算子はタプルを異なる方法で処理しますか?
最初の2つの式は両方ともタプルとして解析されます。
(0, 0) == 0
(False
)、その後に0
が続きます0
、その後に0 == (0, 0)
が続きます(これはまだFalse
です)。式は、等号演算子と比較したカンマ区切りの相対的な優先順位のため、そのように分割されます:Pythonは、2つの式を含むタプルを参照します。タプル。
しかし、3番目の例では、a = 0, 0
cannotはタプルになります。 Tupleは値のコレクションであり、同等性テストとは異なり、Pythonでは割り当てに値がありません。割り当ては式ではなく、ステートメントです。 Tupleまたは他の周囲の式に含めることができる値はありません。タプルとして解釈を強制するために(a = 0), 0
のようなものを試みた場合、構文エラーが発生します。 a = 0, 0
の唯一の有効な解釈として、変数へのTupleの割り当て(a = (0, 0)
を記述することでより明示的にすることができます)を残します。
3つすべてのインスタンスで表示されるのは、言語の 文法仕様 の結果であり、ソースコードで検出されたトークンを解析して解析ツリーを生成する方法です。
この低レベルのコードを見ると、内部で何が起こるかを理解するのに役立ちます。これらのpythonステートメントを取得してバイトコードに変換し、dis
モジュールを使用して逆コンパイルできます。
ケース1:(0, 0) == 0, 0
>>> dis.dis(compile("(0, 0) == 0, 0", '', 'exec'))
1 0 LOAD_CONST 2 ((0, 0))
3 LOAD_CONST 0 (0)
6 COMPARE_OP 2 (==)
9 LOAD_CONST 0 (0)
12 BUILD_Tuple 2
15 POP_TOP
16 LOAD_CONST 1 (None)
19 RETURN_VALUE
(0, 0)
は最初に0
と比較され、False
と評価されます。次に、この結果と最後の0
でTupleが構築されるため、(False, 0)
が取得されます。
ケース2:0, 0 == (0, 0)
>>> dis.dis(compile("0, 0 == (0, 0)", '', 'exec'))
1 0 LOAD_CONST 0 (0)
3 LOAD_CONST 0 (0)
6 LOAD_CONST 2 ((0, 0))
9 COMPARE_OP 2 (==)
12 BUILD_Tuple 2
15 POP_TOP
16 LOAD_CONST 1 (None)
19 RETURN_VALUE
タプルは、最初の要素として0
で構築されます。 2番目の要素については、最初の場合と同じチェックが行われ、False
と評価されるため、(0, False)
が取得されます。
ケース3:(0, 0) == (0, 0)
>>> dis.dis(compile("(0, 0) == (0, 0)", '', 'exec'))
1 0 LOAD_CONST 2 ((0, 0))
3 LOAD_CONST 3 ((0, 0))
6 COMPARE_OP 2 (==)
9 POP_TOP
10 LOAD_CONST 1 (None)
13 RETURN_VALUE
ご覧のとおり、これら2つの(0, 0)
タプルを比較し、True
を返すだけです。
問題を説明する別の方法:あなたはおそらく辞書リテラルに精通している
{ "a": 1, "b": 2, "c": 3 }
および配列リテラル
[ "a", "b", "c" ]
およびタプルリテラル
( 1, 2, 3 )
しかし、あなたが気付いていないのは、辞書や配列リテラルとは異なり、タプルリテラルの周りに通常見られる括弧はリテラル構文の一部ではないことです 。タプルのリテラル構文は、コンマで区切られた一連の式です。
1, 2, 3
( Pythonの正式な文法 )の言語の「exprlist」。
さて、あなたは配列リテラルに何を期待しますか
[ 0, 0 == (0, 0) ]
評価する?それはおそらくもっと似ているようですshouldと同じ
[ 0, (0 == (0, 0)) ]
もちろん[0, False]
と評価されます。同様に、明示的に括弧で囲まれたTupleリテラルを使用
( 0, 0 == (0, 0) )
(0, False)
を取得することは驚くことではありません。ただし、括弧はオプションです。
0, 0 == (0, 0)
同じことです。そして、それが(0, False)
を取得する理由です。
タプルリテラルを囲むかっこがオプションである(why)と思っている場合、それはそのように破壊的な割り当てを記述する必要があるためです。
(a, b) = (c, d) # meh
a, b = c, d # better
アクションが実行される順序の前後に括弧をいくつか追加すると、結果をよりよく理解できる場合があります。
# Build two element Tuple comprising of
# (0, 0) == 0 result and 0
>>> ((0, 0) == 0), 0
(False, 0)
# Build two element Tuple comprising of
# 0 and result of (0, 0) == 0
>>> 0, (0 == (0, 0))
(0, False)
# Create two tuples with elements (0, 0)
# and compare them
>>> (0, 0) == (0, 0)
True
コンマは個別の式に使用されます(もちろん、括弧を使用して異なる動作を強制できます)。リストしたスニペットを表示するとき、カンマ,
はそれを区切り、評価される式を定義します。
(0, 0) == 0 , 0
#-----------|------
expr 1 expr2
タプル(0, 0)
も同様の方法で分類できます。コンマは、リテラル0
で構成される2つの式を区切ります。
最初のPythonでは、次の2つのタプルを作成しています。
(0, 0) == 0
、これはFalse
に評価されます0
2番目の方法では、逆になります。
この例を見てください:
r = [1,0,1,0,1,1,0,0,0,1]
print(r==0,0,r,1,0)
print(r==r,0,1,0,1,0)
その後の結果:
False 0 [1, 0, 1, 0, 1, 1, 0, 0, 0, 1] 1 0
True 0 1 0 1 0
次に、例の最初の数値(0およびr)と比較します。