次の2つのコードスニペットの間に究極の違いはありますか?最初に関数の変数に値を割り当ててから、その変数を返します。 2番目の関数は、値を直接返します。
Pythonそれらを同等のバイトコードに変換しますか?そのうちの1つは高速ですか?
ケース1:
def func():
a = 42
return a
ケース2:
def func():
return 42
いいえ、ありません。
CPythonバイトコードへのコンパイルは、基本的な最適化のみを行うように設計された小さな peepholeオプティマイザー を介してのみ渡されます(これらの最適化の詳細については、テストスイートの test_peepholer.py を参照してください) )。
実際に何が起こるかを見るには、dis
*を使用して生成された命令を確認します。割り当てを含む最初の関数の場合:
from dis import dis
dis(func)
2 0 LOAD_CONST 1 (42)
2 STORE_FAST 0 (a)
3 4 LOAD_FAST 0 (a)
6 RETURN_VALUE
一方、2番目の機能の場合:
dis(func2)
2 0 LOAD_CONST 1 (42)
2 RETURN_VALUE
最初の2つでは、さらに2つの(高速)命令が使用されます: STORE_FAST
および LOAD_FAST
。これらは、現在の実行フレームのfastlocals
配列の値をすばやく保存して取得します。次に、両方の場合で、RETURN_VALUE
が実行されます。そのため、2番目は、実行に必要なコマンドが少ないため、より高速になりますslightly。
一般に、CPythonコンパイラーは、実行する最適化においてconservativeであることに注意してください。そうではなく、そうしようとしない他のコンパイラーと同じくらい賢い(一般に、さらに多くの情報を扱う必要がある)。明らかに正しいことは別として、主な設計目標は、a)シンプルに保ち、b)これらをコンパイルする際に可能な限りSwiftにし、コンパイル段階が存在することすら気付かないようにすることです。
最後に、このような小さな問題に悩まされるべきではありません。速度の利点は小さく、一定であり、Pythonが解釈されるという事実によって生じるオーバーヘッドによって小さくなります。
* dis
は、コードを逆アセンブルする小さなPythonモジュールです。これを使用して、PythonのVMバイトコードを確認できます実行されます。
注:@Jorn Verneeのコメントでも述べられているように、これはPythonのCPython実装に固有のものです。他の実装では、必要に応じてより積極的な最適化を行う場合がありますが、CPythonは行いません。
最初のケースではオブジェクト42
は、単にa
という名前の変数に割り当てられます。つまり、名前(つまり、a
)は値を参照します(つまり、42
)。データをコピーしないという意味で、技術的には割り当てを行いません。
return
ingの間、この名前付きバインディングa
は最初のケースで返され、オブジェクト42
は2番目の場合に返されます。
詳細については、 Ned Batchelderによるこのすばらしい記事 を参照してください。