3

次の 2 つのコード スニペット (A & B) は両方とも、2 つの辞書の積集合を返します。

次の 2 つのコード スニペットは両方とも O(n) で実行され、同じ結果が出力されます。ただし、pythonic であるコード スニペット B は、より高速に実行されるようです。 これらのコード スニペットは Python Cookbook からのものです。

コード スニペット A:

def simpleway():
    result = []
    for k in to500.keys():
          if evens.has_key(k):
                 result.append(k)
    return result

コード スニペット B:

def pythonicsimpleway():
    return [k for k in to500 if k in evens]

いくつかのセットアップロジックと、両方の機能の時間を計るために使用される機能 =>

to500 = {}
for i in range(500): to500[i] = 1
evens = {}
for i in range(0,1000,2): evens[i] = 1

def timeo(fun, n=1000):
    def void(): pass
    start = time.clock()
    for i in range(n): void()
    stend = time.clock()
    overhead = stend - start
    start = time.clock()
    for i in range(n): fun()
    stend = time.clock()
    thetime = stend - start
    return fun.__name__, thetime - overhead

2.3 Ghz Ivy Bridge クアッド コア プロセッサ (OS X 10.8.4) を使用する Python 2.7.5 を使用

私は得る

>>> timeo(simpleway)
('simpleway', 0.08928500000000028)
>>> timeo(pythonicsimpleway)
('pythonicsimpleway', 0.04579400000000078)
4

1 に答える 1

8

彼らはまったく同じことをしているわけではありません。最初のものはより多くの仕事をします:

  • ループのたびにメソッド.has_key()とメソッドを検索して呼び出します。.append()これには、呼び出しごとにスタックのプッシュとポップが必要です。
  • 新しい要素を 1 つずつリストに追加します。Python リストは動的に拡張して、これらの要素のためのスペースを確保する必要があります。

リスト内包表記は、1 回の操作で Python リスト オブジェクトを作成する前に、生成されたすべての要素を C 配列に収集します。

2 つの関数は同じ結果を生成しますが、一方は不必要に遅くなります。

核心的な詳細に進みたい場合は、disモジュールを使用したバイトコードの逆アセンブリを見てください。

>>> dis.dis(simpleway)
  2           0 BUILD_LIST               0
              3 STORE_FAST               0 (result)

  3           6 SETUP_LOOP              51 (to 60)
              9 LOAD_GLOBAL              0 (to500)
             12 LOAD_ATTR                1 (keys)
             15 CALL_FUNCTION            0
             18 GET_ITER            
        >>   19 FOR_ITER                37 (to 59)
             22 STORE_FAST               1 (k)

  4          25 LOAD_GLOBAL              2 (evens)
             28 LOAD_ATTR                3 (has_key)
             31 LOAD_FAST                1 (k)
             34 CALL_FUNCTION            1
             37 POP_JUMP_IF_FALSE       19

  5          40 LOAD_FAST                0 (result)
             43 LOAD_ATTR                4 (append)
             46 LOAD_FAST                1 (k)
             49 CALL_FUNCTION            1
             52 POP_TOP             
             53 JUMP_ABSOLUTE           19
             56 JUMP_ABSOLUTE           19
        >>   59 POP_BLOCK           

  6     >>   60 LOAD_FAST                0 (result)
             63 RETURN_VALUE        
>>> dis.dis(pythonicsimpleway)
  2           0 BUILD_LIST               0
              3 LOAD_GLOBAL              0 (to500)
              6 GET_ITER            
        >>    7 FOR_ITER                24 (to 34)
             10 STORE_FAST               0 (k)
             13 LOAD_FAST                0 (k)
             16 LOAD_GLOBAL              1 (evens)
             19 COMPARE_OP               6 (in)
             22 POP_JUMP_IF_FALSE        7
             25 LOAD_FAST                0 (k)
             28 LIST_APPEND              2
             31 JUMP_ABSOLUTE            7
        >>   34 RETURN_VALUE        

繰り返しごとのバイトコード命令の数は、明示的な for ループの方がはるかに多くなります。simplewayループは反復ごとに 11の命令を実行する必要があります ( .has_key()True の場合) に対して、リスト内包表記の場合は 7 です。LOAD_ATTRCALL_FUNCTION

最初のバージョンを高速化したい場合は.has_key()inテストに置き換え、辞書を直接ループし、.append()属性をローカル変数にキャッシュします。

def simpleway_optimized():
    result = []
    append = result.append
    for k in to500:
        if k in evens:
            append(k)
    return result

次に、timeitモジュールを使用してタイミングを適切にテストします (繰り返し実行、プラットフォームの最も正確なタイマー)。

>>> timeit('f()', 'from __main__ import evens, to500, simpleway as f', number=10000)
1.1673870086669922
>>> timeit('f()', 'from __main__ import evens, to500, pythonicsimpleway as f', number=10000)
0.5441269874572754
>>> timeit('f()', 'from __main__ import evens, to500, simpleway_optimized as f', number=10000)
0.6551430225372314

ここでsimpleway_optimizedは、リスト内包表記法に速度で近づいていますが、後者は、Python リスト オブジェクトを 1 ステップで構築することで勝つことができます。

于 2013-06-18T21:37:12.270 に答える