10

Python では、次のように関数をネストすることができます。

def do_something():
    def helper():
        ....
    ....

Python がこのケースをよりスマートに処理しない限り、を使用helperするたびに新しく作成する必要がありdo_somethingます。実際、メイン関数の外側にヘルパー関数を作成する代わりにこれを行うと、パフォーマンスが低下しますか?もしそうなら、それはどれほど素晴らしいですか?

4

2 に答える 2

9

はい、メイン関数内でヘルパーを宣言すると、個別に宣言するよりも遅くなります。

### test_nested.py ###
import timeit
def foo():
    def bar():
        pass
    pass
print(timeit.timeit("foo()", setup="from __main__ import foo"))

### test_flat.py ###
import timeit
def foo():
    pass
def bar():
    pass
print(timeit.timeit("foo()", setup="from __main__ import foo, bar"))


### Shell ###
✗ python3 ./test_flat.py
0.42562198638916016
✗ python3 ./test_nested.py
0.5836758613586426

これは、約 30% の速度低下です。この些細なケースでは、関数の作成と呼び出しがインタープリターのすべてであることを思い出してください。実際の使用では、違いははるかに少なくなります。

于 2012-12-27T13:57:39.253 に答える
4

パフォーマンスのペナルティは確実に存在します。関数が別の関数の呼び出し内で作成された場合、関数オブジェクトは、外側の関数が呼び出されるたびに実際に作成されます。しかし、そのペナルティは小さく、通常は無視できます。特に明白な事実を考慮に入れると、ほとんどの場合、ネストされた関数は外部に配置できない場合にのみ作成する必要があります。

ネストされた関数が必要になる理由は、ネストされた関数内の外側の関数のスコープ変数にアクセスする必要があるためです。通常、これは外部関数から内部関数オブジェクトを直接的または間接的に返す (デコレータのように) か、内部関数をコールバックとしてどこかに渡すことにつながります。ネストされた関数によってアクセスされる変数は、ネストされた関数オブジェクトが破棄されるまで存在し、それぞれが異なるスコープ インスタンスから変数を参照するため、ネストされた関数のインスタンスごとに異なります。

私の考えでは、空の内部関数を作成するのに必要な時間と、外部に配置された同じ関数を使用するのに必要な時間を比較するだけではほとんど意味がありません. パフォーマンスの違いは、純粋にコードの動作の違いから生じます。望ましいコード動作は、関数を配置する場所を選択する必要があるものです。

ちょっとした例:

def outer(n):
    v1 = "abc%d" % n
    v2 = "def"
    def inner():
        print locals().keys()
        return v1
    v1 = "_" + v1
    return inner
f1 = outer(1)
f2 = outer(2)
print f1()
print f2()

出力は次のとおりです。

['v1']
_abc1
['v1']
_abc2

主な瞬間:

  1. 内部関数の locals() には、それが使用する外部関数のローカルのみが含まれます (v1 で、v2 は含まれません)。

  2. v1 は、関数オブジェクトの作成後に変更されます。ただし、v1 の型が不変 (str) であっても、変更は内部関数に表示されます。したがって、内部関数が参照するのは、関数オブジェクトの作成時に格納された参照だけではなく、外部関数のローカルの実際のサブセットです。幸い、内部関数オブジェクトの存在は、v1 以外のスコープ変数の破棄を妨げません。v2 の値を、破棄されたときに何かを出力するオブジェクトに置き換えると、外部関数が終了するとすぐにメッセージが出力されます。

  3. inner() の異なるインスタンスは、単一の外部スコープ インスタンスを共有しません。v1 の値は異なります。

これらすべての効果は、ネストされた関数を使用しないと実現できません。ネストされた関数を使用する必要があるのはそのためです。実際、パフォーマンスが低下することはありません。余分な動作には余分な時間が必要です。追加の動作が必要な場合は、ネストされた関数を使用する必要があります。必要ないなら、しなくていい。

于 2012-12-28T09:30:15.820 に答える