関数定義によるパフォーマンスへの影響はごくわずかであり、ローカル変数の定義に匹敵します。
関数の本体は1回だけコンパイルされ、コードブロックの実行中にコンパイルされたブロック(LOAD_CONST
)をロードするだけで、バイトコードの結果がMAKE FUNCTION
ローカル変数に格納されます。
>>> import dis
>>> def foo():
... def bar():
... pass
... print 'boo!'
...
>>> dis.dis(foo)
2 0 LOAD_CONST 1 (<code object bar at 0x106c447b0, file "<stdin>", line 2>)
3 MAKE_FUNCTION 0
6 STORE_FAST 0 (bar)
4 9 LOAD_CONST 2 ('boo!')
12 PRINT_ITEM
13 PRINT_NEWLINE
14 LOAD_CONST 0 (None)
17 RETURN_VALUE
ここで、ネストされた関数を含む関数を何千回も呼び出すと、そのMAKE_FUNCTION
操作のパフォーマンスへの影響に気付くでしょう。
>>> import timeit
>>> def nonlocal(): pass
...
>>> def callnonlocal(): nonlocal()
...
>>> def calllocal():
... def localf(): pass
... localf()
...
>>> timeit.timeit('callnonlocal()', 'from __main__ import callnonlocal')
0.39106082916259766
>>> timeit.timeit('calllocal()', 'from __main__ import calllocal')
0.4878239631652832
ただし、関数に実際のコードを入れるほど、その差は小さくなることに注意してください。MAKE_FUNCTION
上記の例は非常に工夫されており、実行時間に対するバイトコードの影響のみに焦点を当てています。
まず、読みやすさと保守性を最適化します。