2

これは、for ループとリスト内包表記で同じ辞書アイテムを複数回参照することを示すために考案された例です。まず、for ループ:

dict_index_mylists = {0:['a', 'b', 'c'], 1:['b', 'c', 'a'], 2:['c', 'a', 'b']}

# for-loop
myseq = []
for i in [0, 1, 2]:
    interim = dict_index_mylists[i]
    if interim[0] == 'b' or interim[1] == 'c' or interim[2] == 'a':    
        myseq.append(interim)

for ループでは、暫定リストがディクショナリ オブジェクトから参照され、次に if 条件で複数回参照されます。これは、特にディクショナリが非常に大きい場合やストレージ上にある場合に意味があります。繰り返しになりますが、Python 辞書はパフォーマンスのために最適化されているため、「暫定」参照はおそらく不要です。

これは、for ループのリスト内包表記です。

# list-comprehension
myseq = [dict_index_mylists[i] for i in [0, 1, 2] if dict_index_mylists[i][0] == 'b' or dict_index_mylists[i][1] == 'c' or dict_index_mylists[i][2] == 'a']

質問は次のとおりです。

を。リスト内包表記は、ディクショナリ アイテムへの複数の参照を作成しますか、それともローカルの「暫定」リストを参照して保持しますか?

b. 同じ辞書項目に複数の条件を含み、辞書が非常に大きい場合に最適なリスト内包表記は何ですか?

4

3 に答える 3

1

一般的な部分式の最適化についてのみ質問しているようです。リスト内包表記では、辞書に複数回インデックスを付けます。Python は動的であり、操作のような副作用がどのようなものかを知るのは難しいdict_index_mylists[i]ため、CPython は指示された回数だけ操作を実行します。

PyPy などの他の実装では JIT を使用し、部分式を最適化することがありますが、それが何をするのかを事前に知ることは困難です。

パフォーマンスに非常に関心がある場合は、さまざまなオプションの時間を計って、どれが最適かを確認する必要があります。

于 2012-07-02T12:34:47.207 に答える
0

私は python バイトコードを調べる専門家ではありませんが、今朝何か新しいことを学ぼうと試みたのは次のとおりです。

def dostuff():
    myseq = [dict_index_mylists[i] for i in [0, 1, 2] if dict_index_mylists[i][0] == 'b' or dict_index_mylists[i][1] == 'c' or dict_index_mylists[i][2] == 'a']

import dis
dis.dis(dostuff)

出力 (以下) を見ると、 への 4 つの呼び出しがLOAD_GLOBALあるため、python が中間リストを格納しているようには見えません。2 番目の質問についてですが、あなたが持っているものは、おそらくあなたができる限りのことです。とはいえ、思ったほど悪くはありません。 dictオブジェクトはハッシュ関数によって項目にアクセスするため、検索の複雑さはO(1)辞書のサイズに関係ありません。もちろん、timeit2 つの実装 (loop と list-comp を使用) を常に使用して比較し、より高速な方を選択することもできます。プロファイリングは(いつものように)あなたの友達です。

付録 (dis.dis(dostuff) の出力)

5           0 BUILD_LIST               0
            3 DUP_TOP             
            4 STORE_FAST               0 (_[1])
            7 LOAD_CONST               1 (0)
           10 LOAD_CONST               2 (1)
           13 LOAD_CONST               3 (2)
           16 BUILD_LIST               3
           19 GET_ITER            
      >>   20 FOR_ITER                84 (to 107)
           23 STORE_FAST               1 (i)
           26 LOAD_GLOBAL              0 (dict_index_mylists)
           29 LOAD_FAST                1 (i)
           32 BINARY_SUBSCR       
           33 LOAD_CONST               1 (0)
           36 BINARY_SUBSCR       
           37 LOAD_CONST               4 ('b')
           40 COMPARE_OP               2 (==)
           43 JUMP_IF_TRUE            42 (to 88)
           46 POP_TOP             
           47 LOAD_GLOBAL              0 (dict_index_mylists)
           50 LOAD_FAST                1 (i)
           53 BINARY_SUBSCR       
           54 LOAD_CONST               2 (1)
           57 BINARY_SUBSCR       
           58 LOAD_CONST               5 ('c')
           61 COMPARE_OP               2 (==)
           64 JUMP_IF_TRUE            21 (to 88)
           67 POP_TOP             
           68 LOAD_GLOBAL              0 (dict_index_mylists)
           71 LOAD_FAST                1 (i)
           74 BINARY_SUBSCR       
           75 LOAD_CONST               3 (2)
           78 BINARY_SUBSCR       
           79 LOAD_CONST               6 ('a')
           82 COMPARE_OP               2 (==)
           85 JUMP_IF_FALSE           15 (to 103)
      >>   88 POP_TOP             
           89 LOAD_FAST                0 (_[1])
           92 LOAD_GLOBAL              0 (dict_index_mylists)
           95 LOAD_FAST                1 (i)
           98 BINARY_SUBSCR       
           99 LIST_APPEND         
          100 JUMP_ABSOLUTE           20
      >>  103 POP_TOP             
          104 JUMP_ABSOLUTE           20
      >>  107 DELETE_FAST              0 (_[1])
          110 STORE_FAST               2 (myseq)
          113 LOAD_CONST               0 (None)
          116 RETURN_VALUE 
于 2012-07-02T11:42:54.857 に答える
0

最初のポイント: 何も ('myseq' を期待) ここでは "作成" されません。コードの forloop でも listcomp バージョンでもありません。既存の dict アイテムへの単なる参照です。

ここで質問に答えます: リスト comp バージョンはルックアップを行います (dict.__getitem__dict_index_mylists[i]式の呼び出し。これらの各ルックアップは同じリストへの参照を返します。ローカル参照を保持することで、これらの余分なルックアップを回避できます。辞書の項目、つまり:

myseq = [
    item for item in (dict_index_mylists[i] for i in (0, 1, 2)) 
    if item[0] == 'b' or item[1] == 'c' or item[2] == 'a'
    ]

しかし、リストコンプを書くためだけにリストコンプを書いても意味がありません。

元の順序を気にせず、これを辞書全体に適用したい場合は、を使用dict.itervalues()する方が簡単になることに注意してください。

wrt/ 2 番目の質問、「最適」は絶対的なものではありません。何を最適化しますか? スペース ?時間 ?可読性?

于 2012-07-02T11:44:03.220 に答える