在Python中嵌套函数会有开销吗?
在Python中,如果我在一个父函数里面定义了一个子函数,每次调用父函数的时候,子函数都是“初始化”(创建)一次吗?这样把一个函数放在另一个函数里面,会不会影响性能?
6 个回答
21
确实会有一些影响,但在大多数情况下,这种影响非常小,你不需要太担心。大部分复杂的应用程序可能已经有了性能瓶颈,而这些瓶颈的影响要比这个大得多。与其担心性能,不如关注代码的可读性和可重用性。
这里有一段代码,用来比较在循环中每次重新定义一个函数和重复使用一个已经定义好的函数的性能。
import gc
from datetime import datetime
class StopWatch:
def __init__(self, name):
self.name = name
def __enter__(self):
gc.collect()
self.start = datetime.now()
def __exit__(self, type, value, traceback):
elapsed = datetime.now()-self.start
print '** Test "%s" took %s **' % (self.name, elapsed)
def foo():
def bar():
pass
return bar
def bar2():
pass
def foo2():
return bar2
num_iterations = 1000000
with StopWatch('FunctionDefinedEachTime') as sw:
result_foo = [foo() for i in range(num_iterations)]
with StopWatch('FunctionDefinedOnce') as sw:
result_foo2 = [foo2() for i in range(num_iterations)]
当我在我的Macbook Air上运行这段代码,使用的是OS X Lion和Python 2.7时,我得到了:
** Test "FunctionDefinedEachTime" took 0:00:01.138531 **
** Test "FunctionDefinedOnce" took 0:00:00.270347 **
66
这个代码对象是提前编译好的,所以这一部分没有额外的负担。每次调用函数的时候,函数对象都会被创建,它会把函数的名字和代码对象绑定在一起,还会记录一些默认的变量等等。
简单总结一下:这不是免费的。
>>> from dis import dis
>>> def foo():
def bar():
pass
return bar
>>> dis(foo)
2 0 LOAD_CONST 1 (<code object bar at 0x1017e2b30, file "<pyshell#5>", line 2>)
3 MAKE_FUNCTION 0
6 STORE_FAST 0 (bar)
4 9 LOAD_FAST 0 (bar)
12 RETURN_VALUE
53
是的,每次都会创建一个新的对象。除非你在一个很紧凑的循环里使用它,否则这通常不是个大问题。通过性能分析可以告诉你这是否会造成问题。
In [80]: def foo():
....: def bar():
....: pass
....: return bar
....:
In [81]: id(foo())
Out[81]: 29654024
In [82]: id(foo())
Out[82]: 29651384