46

私は最近、Python 2.7 から Python 3.3 に切り替えました。Python 2 では、辞書キーの順序付けは任意ですが一貫していたようですが、Python 3 では、eg で取得した辞書のキーの順序付けはvars()非決定的であるように見えます。

私が実行した場合:

class Test(object): pass
parameters = vars(Test)
print(list(parameters.keys()))

Python 2.7 と Python 3.3 の両方で、次のようにします。

  • Python 2.7は一貫して私に与えます

    ['__dict__', '__module__', '__weakref__', '__doc__']
    
  • Python 3.3 では、ランダムな順序を取得できます。たとえば、次のようになります。

    ['__weakref__', '__module__', '__qualname__', '__doc__', '__dict__']
    ['__doc__', '__dict__', '__qualname__', '__module__', '__weakref__']
    ['__dict__', '__module__', '__qualname__', '__weakref__', '__doc__']
    ['__weakref__', '__doc__', '__qualname__', '__dict__', '__module__']
    

この非決定性はどこから来るのでしょうか? そして、なぜのようなものです

list({str(i): i for i in range(10)}.keys())

…実行間で一貫性があり、常に与える

['3', '2', '1', '0', '7', '6', '5', '4', '9', '8']

… ?

4

2 に答える 2

61

更新: Python 3.6 では、挿入順序を保持dictする新しい実装があります。Python 3.7 以降、この順序を維持する動作が保証されています。

dictオブジェクトの挿入順序保存の性質は、Python 言語仕様の公式部分であると宣言されています。


これは、 Python 3.3でデフォルトで有効になっていた 2012 年のセキュリティ修正の結果です(「セキュリティの改善」まで下にスクロールします)。

発表より:

ハッシュのランダム化により、辞書とセットの反復順序が予測不能になり、Python の実行ごとに異なります。Python は dict または set 内のキーの反復順序を保証していないため、アプリケーションはそれに依存しないことをお勧めします。歴史的に、ディクテーションの反復順序はリリース間で頻繁に変更されることはなく、Python の連続実行間で常に一貫性が保たれてきました。したがって、一部の既存のアプリケーションは、dict または set の順序付けに依存している可能性があります。これと、信頼されていない入力を受け入れない多くの Python アプリケーションはこの攻撃に対して脆弱ではないという事実のために、ここで言及されているすべての安定した Python リリースでは、ハッシュのランダム化はデフォルトで無効になっています。

上記のように、最後の大文字のビットは Python 3.3 では当てはまりません。

参照: object.__hash__()ドキュメント(「メモ」サイドバー)。

絶対に必要な場合は、PYTHONHASHSEED環境変数を に設定して、この動作の影響を受けるバージョンの Python でハッシュのランダム化を無効にすることができます0


あなたの反例:

list({str(i): i for i in range(10)}.keys())

…実際、Python 3.3 では常に同じ結果が得られるわけではありませんが、ハッシュの衝突が処理される方法により、異なる順序の数は制限されています。

$ for x in {0..999}
> do
>   python3.3 -c "print(list({str(i): i for i in range(10)}.keys()))"
> done | sort | uniq -c
     61 ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
     73 ['1', '0', '3', '2', '5', '4', '7', '6', '9', '8']
     62 ['2', '3', '0', '1', '6', '7', '4', '5', '8', '9']
     59 ['3', '2', '1', '0', '7', '6', '5', '4', '9', '8']
     58 ['4', '5', '6', '7', '0', '1', '2', '3', '8', '9']
     55 ['5', '4', '7', '6', '1', '0', '3', '2', '9', '8']
     62 ['6', '7', '4', '5', '2', '3', '0', '1', '8', '9']
     63 ['7', '6', '5', '4', '3', '2', '1', '0', '9', '8']
     60 ['8', '9', '0', '1', '2', '3', '4', '5', '6', '7']
     66 ['8', '9', '2', '3', '0', '1', '6', '7', '4', '5']
     65 ['8', '9', '4', '5', '6', '7', '0', '1', '2', '3']
     53 ['8', '9', '6', '7', '4', '5', '2', '3', '0', '1']
     62 ['9', '8', '1', '0', '3', '2', '5', '4', '7', '6']
     52 ['9', '8', '3', '2', '1', '0', '7', '6', '5', '4']
     73 ['9', '8', '5', '4', '7', '6', '1', '0', '3', '2']
     76 ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0']

この回答の冒頭で述べたように、Python 3.6 ではそうではなくなりました。

$ for x in {0..999}
> do
>   python3.6 -c "print(list({str(i): i for i in range(10)}.keys()))"
> done | sort | uniq -c
   1000 ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
于 2013-02-19T13:42:09.810 に答える
13

ただし、Python 3.7 にはまだ非決定論的なセットがあることに注意してください。辞書は挿入順序を保持しますが、セットは保持しません。セットは、同じランダムな動作を示すことがあります。

python3 -c "print({str(i) for i in range(9)})"

それでも、実行ごとに異なる結果が得られます。

于 2019-02-06T01:36:07.197 に答える