在 Python 中,可以嵌套这样的函数:
def do_something():
def helper():
....
....
除非 Python 更巧妙地处理这种情况,否则helper
每次do_something
使用时都必须重新创建。实际上这样做是否会对性能造成影响,而不是在主函数之外创建辅助函数,如果是,它有多好?
在 Python 中,可以嵌套这样的函数:
def do_something():
def helper():
....
....
除非 Python 更巧妙地处理这种情况,否则helper
每次do_something
使用时都必须重新创建。实际上这样做是否会对性能造成影响,而不是在主函数之外创建辅助函数,如果是,它有多好?
是的,在 main 函数中声明一个助手比单独声明它们要慢:
### 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% 的放缓。请记住,在这种简单的情况下,创建和调用函数是解释器所做的一切。在任何实际使用中,差异都会小得多。
性能损失肯定存在。如果一个函数是在对另一个函数的调用中创建的,那么每次调用外部函数时都会真正创建函数对象。但这种惩罚很小,通常可以忽略不计。特别是考虑到一个明显的事实:在大多数情况下,只有当它不能放在外面时,您才应该创建一个嵌套函数。
您可能需要嵌套函数的原因是需要在嵌套函数内访问外部函数的范围变量。通常这将导致直接或间接地从外部函数返回内部函数对象(就像在装饰器中一样),或者,也许,将内部函数作为回调传递给某处。嵌套函数访问的变量将一直存在,直到嵌套函数对象被销毁,并且对于嵌套函数的不同实例,它们将是不同的,因为每个实例都看到来自不同范围实例的变量。
在我看来,仅仅将创建一个空的内部函数所需的时间与使用放置在外部的相同函数进行比较几乎是没有意义的。性能差异纯粹源于代码行为的差异。所需的代码行为应该使您选择放置函数的位置。
只是一个小插图:
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
关键时刻:
内部函数的 locals() 仅包括它使用的外部函数 locals(v1,但不包括 v2)。
v1 在创建函数对象后更改。但是,即使 v1 的类型是不可变的 (str),内部函数仍然可以看到更改。因此,内部函数看到的是外部函数局部变量的真正子集,而不仅仅是在函数对象创建时存储的引用。幸运的是,内部函数对象的存在并不能阻止 v1 以外的范围变量被破坏。如果我将 v2 值替换为在被销毁时打印某些内容的对象,它会在外部函数退出时立即打印消息。
inner() 的不同实例不共享单个外部范围实例:v1 值不同。
如果不使用嵌套函数,所有这些效果根本无法实现。这就是为什么应该使用嵌套函数的原因,实际上并没有性能损失:额外的行为需要额外的时间。如果您需要这种额外的行为,您应该使用嵌套函数。如果你不需要它,你不应该。