嵌套函数的性能开销是多少?

时间:2012-12-27 13:57:39

标签: python performance function

在Python中,可以嵌套这样的函数:

def do_something():
    def helper():
        ....
    ....

除非Python更巧妙地处理这种情况,否则每次使用helper时都必须重新创建do_something。事实上,这样做会影响性能,而不是在主要功能之外创建辅助功能,如果是的话,它有多棒?

2 个答案:

答案 0 :(得分:5)

是的,在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%的放缓。请记住,在这个简单的案例中,创建和调用函数都是解释器所做的。在任何实际使用中,差异都会小得多。

答案 1 :(得分:2)

性能损失肯定存在。如果在调用另一个函数内部创建函数,则每次调用外部函数时都会创建函数对象。但是这种惩罚很小,通常可能会被忽略。特别是考虑到一个显而易见的事实:在大多数情况下,只有在无法放在外面时才应创建嵌套函数。

您可能需要拥有嵌套函数的原因是需要访问嵌套函数中的外部函数的范围变量。通常,这将导致直接或间接地从外部函数返回内部函数对象(如在装饰器中),或者,可能将内部函数作为回调传递到某处。嵌套函数访问的变量将一直存在,直到嵌套的函数对象被销毁,并且对于嵌套函数的不同实例它们将是不同的,因为每个实例都看到来自不同范围实例的变量。

在我看来,只需将创建空内部函数所需的时间与使用外部相同函数进行比较几乎是没有意义的。性能差异纯粹源于代码行为的差异。所需的代码行为应该让您选择放置函数的位置。

只是一个小例子:

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

关键时刻:

  1. 内部函数的locals()仅包含它使用的外部函数locals(v1,但不包括v2)。

  2. 创建函数对象后更改v1。但是,即使v1的类型是不可变的(str),内部函数仍然可以看到这些更改。因此,内部函数看到的是外部函数本地的一个真实子集,而不仅仅是函数对象创建时存储的引用。幸运的是,内部函数对象的存在并不会阻止v1以外的范围变量被破坏。如果我将v2值替换为在销毁时打印某些内容的对象,则会在外部函数退出时立即打印该消息。

  3. inner()的不同实例不共享单个外部范围实例:v1值不同。

  4. 如果不使用嵌套函数,就无法实现所有这些效果。这就是为什么应该使用嵌套函数,实际上没有性能损失:额外的行为需要额外的时间。如果您需要额外的行为,则应使用嵌套函数。如果你不需要它,你就不应该。