6

ループ内で変化しない条件を確認する必要がある場合があります。これは、テストが反復ごとに評価されることを意味しますが、これは正しい方法ではないと思います。

ループ内で条件が変わらないので、ループ外で一度だけテストする必要があると思いましたが、「自分自身を繰り返す」必要があり、おそらく同じループを複数回記述する必要があります。ここに私が何を意味するかを示すコードがあります:

#!/usr/bin/python

x = True      #this won't be modified  inside the loop
n = 10000000

def inside():
    for a in xrange(n):
        if x:    #test is evaluated n times
            pass
        else:
            pass
    
def outside():
    if x:        #test is evaluated only once
        for a in xrange(n):  
            pass
    else:
        for a in xrange(n):
            pass

if __name__ == '__main__':
    outside()
    inside()

前のコードで実行cProfileすると、次の出力が得られました。

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.542    0.542    0.542    0.542 testloop.py:5(inside)
        1    0.261    0.261    0.261    0.261 testloop.py:12(outside)
        1    0.000    0.000    0.803    0.803 testloop.py:3(<module>)

これは明らかに、ループの外側で 1 回テストするとパフォーマンスが向上することを示していますが、同じループを 2 回 (いくつかelifs がある場合はさらに多く) 記述しなければなりませんでした。

ほとんどの場合、このパフォーマンスは問題にならないことはわかっていますが、この種のコードを記述する最善の方法を知る必要があります。たとえば、python にテストを 1 回だけ評価するように指示する方法はありますか?

どんな助けでも大歓迎です、ありがとう。

編集:

実際にいくつかのテストを行った後、パフォーマンスの違いは主に、テストの評価ではなく、ループ内で実行される他のコードの影響を受けると確信しました。したがって、今のところ、最初の形式に固執しています。この形式の方が読みやすく、後でデバッグするのに適しています。

4

7 に答える 7

5

まず、例間のパフォーマンスの違いの主な要素は、グローバルの検索にかかる時間です。それをローカル変数にキャプチャすると、次のようになります。

def inside_local():
    local_x = x
    for a in xrange(n):
        if local_x:
            pass
        else:
            pass

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.258    0.258    0.258    0.258 testloop.py:13(outside)
    1    0.314    0.314    0.314    0.314 testloop.py:21(inside_local)
    1    0.421    0.421    0.421    0.421 testloop.py:6(inside)

パフォーマンスの違いはほとんどなくなります。

一般に、共通コードがある場合は常に、それをカプセル化するようにしてください。の分岐にifループ以外の共通点がない場合は、ループ イテレータをジェネレータなどにカプセル化してみてください。

于 2012-06-20T08:49:46.033 に答える
5

これは、私が通常この状況で行うことです。

def inside():
    def x_true(a):
        pass

    def x_false(a):
        pass

    if x:
        fn = x_true
    else:
        fn = x_false

    for a in xrange(n):
        fn(a)
于 2012-06-20T09:05:49.687 に答える
3

Pythonには、クロージャ、ラムダ関数、関数にファーストクラスのステータスを与える、多くの組み込み関数などがあります。これは、重複したコードを削除するのに本当に役立ちます。たとえば、値のシーケンスに関数を適用する必要があると想像してみてください。こちらです

def outside():              
    if x:        # x is a flag or it could the function itself, or ...
        fun = sum # calc the sum, using pythons, sum function
    else:
        fun = lambda values: sum(values)/float(len(values)) # calc avg using our own function

    result = fun(xrange(101))

正確なシナリオを提供していただければ、それを最適化するお手伝いをいたします。

于 2012-06-20T09:36:08.380 に答える
2

私はその方向でサポートを提供するインタープリター言語を知っていません.コンパイルされた言語は比較を1回だけ行う可能性があります(ループ不変最適化)が、xの評価が単純な場合、これはあまり役に立ちません. pass ステートメントの代わりに配置するコードを完全に同一にすることはできません。その場合、「if」は役に立たないからです。通常、両方の場所で呼び出されるプロシージャを作成します。

于 2012-06-20T08:49:37.380 に答える
1
def outside():
    def true_fn(a):
        pass
    def false_fn(a):
        pass

    fn = true_fn if x else false_fn
    for a in xrange(n):
        fn(a)
于 2012-06-20T09:54:39.173 に答える
0

多くのシステム リソースを消費せずに x の値をテストしたいという最初の質問に基づいて、グローバル x の値をローカル変数にコピーすることを含む回答を既に受け入れています。

ここで、x の値を返すのに複数ステップの関数が含まれていても、x の結果が常に同じになることが保証されている場合は、関数のメモ化を検討します。これは、件名に関する非常に優れたstackoverflowリンクです

于 2012-06-20T18:44:46.260 に答える
0

あなたの場合、読みやすさやパフォーマンスなど、必要なものによって異なります。

実行しているタスクが何らかのフィルターである場合は、 a を使用しlist_comprehensionてループを実行することもできます。

[e for e in xrange(n) if x]

コードをもう少し見せていただければ、何か提案できます。

于 2012-06-20T08:41:25.020 に答える