2016-10-24 3 views
55

set関数呼び出しはダーティを消去しますが、セットリテラルを解析するのはなぜですか?set literalは、set関数呼び出しとは異なる結果を返します。

>>> x = Decimal('0') 
>>> y = complex(0,0) 
>>> set([0, x, y]) 
{0} 
>>> {0, x, y} 
{Decimal('0'), 0j} 

(パイソン2.7.12。this同様の質問の場合とおそらく同じ根本原因)平等のための

+0

関連:[Dictの/設定の解析順序一貫性](https://stackoverflow.com/q/34623846/4279) – jfs

答えて

53

設定のテスト、およびPythonの新しいリリースがあるまで、彼らはこれを行う順序は、ベースと異なる場合がありますフォーム上では、以下に示すように、構築されているセットに値を渡します。 0 == x以来

は真0 == yで真であるが、x == yあるセットは最初の2つのテストがあまりにも本当だったらx == yが真でなければならないことを前提としていて、ここでの行動は、本当に未定義です。リテラルを反転させるための

>>> set([y, x, 0]) 
set([0j, Decimal('0')]) 

と同じ:あなたはset()に渡されたリストを逆場合は平等の順序が変更をテストするため

は、あなたは、リテラルを使用するのと同じ出力を得る

何が起こっている
>>> {y, x, 0} 
set([0]) 

セットリテラルがスタックに値をロードし、スタック値は新しいセットオブジェクトに追加されていることです逆の順序で。

限り最初をロードさ0として、他の二つのオブジェクトは、その後、セットに既に0に対して試験されます。他の二つのオブジェクトのいずれかが最初にロードされた瞬間は、平等のテストが失敗し、2つのオブジェクトが追加されます:

リテラルは逆に要素を追加設定
>>> {y, 0, x} 
set([Decimal('0'), 0j]) 
>>> {x, 0, y} 
set([0j, Decimal('0')]) 

構文をサポートするのPythonのすべてのバージョンでのバグが存在します、Python 2.7.12と3.5.2までずっと。これは最近修正されました。issue 26020(2.7.13,3.5.3および3.6の一部ですが、まだリリースされていません)を参照してください。バイトコードは、(最初​​のスタックに0を押して)逆の順序でスタックに要素を追加しながら、

# oparg is the number of elements to take from the stack to add 
for (; --oparg >= 0;) { 
    w = POP(); 
    if (err == 0) 
     err = PySet_Add(x, w); 
    Py_DECREF(w); 
} 

:あなたは2.7.12を見れば、あなたはBUILD_SET in ceval.cはトップダウンから、スタックを読み込むことがわかります

>>> from dis import dis 
>>> dis(compile('{0, x, y}', '', 'eval')) 
    2   0 LOAD_CONST    1 (0) 
       3 LOAD_GLOBAL    0 (x) 
       6 LOAD_GLOBAL    1 (y) 
       9 BUILD_SET    3 
      12 RETURN_VALUE 

修正点は、スタックから要素を逆順に読み取ることです。 Python 2.7.13 versionは、(その後スタックから要素を削除するとSTACKADJ()PEEK()の代わりPOP()を使用しています。

for (i = oparg; i > 0; i--) { 
    w = PEEK(i); 
    if (err == 0) 
     err = PySet_Add(x, w); 
    Py_DECREF(w); 
} 
STACKADJ(-oparg); 

平等テストの問題が他の質問と同じ根本的な原因があります。 Decimal()クラスはここではcomplexと同等の問題を抱えていますが、これはPython 3.2で修正されました(Decimal() support comparisons to complex and a few other numeric types it didn't support beforeとする)。

+10

あなたはそれが平等これら3つのタイプのために推移されていないことをバグだと思いますか? –

+7

@StevenRumbalski:そうだと思います。 '0j == 0'が真で、' Decimal( '0')== 0'も真であるとき、 '0j'が' Decimal( '0') 'に等しくないのはなぜですか? Python 3.2では、これを 'Decimal'比較を更新することで修正しました。 –

+2

'bytearray( 'ab')== 'ab' == u'ab''もまた過渡性を壊していますが、' bytearray'はハッシュ可能ではないので、そこには何の影響もありません。 – wim

7

あなたが発見したバグと組み合わせて、セットが構築される順番はすべてyour other questionです。リテラルはリストとは逆の順序で構成されているようです。

>>> {0, x, y} 
set([0j, Decimal('0')]) 
>>> {y, x, 0} 
set([0]) 
+0

ああ、はい、それは洞察力があります。どのようにリテラルが右から左に解析されるかを示すcpythonソース参照はどうでしょうか? – wim

+0

@wim:バグが最近修正されたために、コードを見つけるのが難しかったです。 –

+0

@wimその音が楽しいものとして、私はPythonのソースコードを介してspelunkingに行ったことはありません。 –

関連する問題