性能惩罚肯定存在。如果函数是在对另一个函数的调用中创建的,那么每次调用外部函数时都会创建函数对象。但这种惩罚很小,而且通常可以忽略不计。尤其要考虑到一个显而易见的事实:在大多数情况下,只有当嵌套函数不能放在外部时,才应该创建它。在
可能需要嵌套函数的原因是需要访问嵌套函数内部的外部函数的作用域变量。通常这会导致直接或间接地从外部函数返回内部函数对象(比如在decorators中),或者,可能会导致将内部函数作为回调函数传递到某个地方。嵌套函数访问的变量将一直存在,直到嵌套函数对象被销毁为止,并且由于每个实例都看到来自不同范围实例的变量,因此它们对于嵌套函数的不同实例也会有所不同。在
在我看来,仅仅比较创建一个空的内部函数所需的时间与使用放置在外部的相同函数几乎毫无意义。性能差异纯粹是由代码行为的差异引起的。所需的代码行为应该使您选择将函数放置在何处。在
只是一个小例子: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()
输出为:
^{pr2}$
关键时刻:内部函数的locals()只包含它使用的外部函数局部变量(v1,而不是v2)。
v1在函数对象创建后更改。然而,即使v1的类型是不可变的(str),内部函数仍然可以看到这些变化。因此,内部函数看到的是外部函数局部变量的真实子集,而不仅仅是在创建函数对象时存储的引用。幸运的是,内部函数对象的存在并不能阻止v1以外的范围变量被破坏。如果我用销毁时打印的对象替换v2值,当外部函数退出时,它会立即打印消息。
inner()的不同实例不共享单个外部作用域实例:v1值不同。
如果不使用嵌套函数,则无法实现所有这些效果。这就是为什么应该使用嵌套函数,事实上没有性能损失:额外的行为需要额外的时间。如果需要额外的行为,应该使用嵌套函数。如果你不需要它,你不应该。在