内部函数每次调用其父函数时都会重新定义吗?

问题描述 投票:0回答:1

我知道 Python 中的函数是一等公民,这意味着它们是

function
类的对象,类似于
5
int
类的对象。这意味着在它们生命周期的某个时刻会调用构造函数。对于大多数函数,我希望在定义它们时会发生这种情况(因为大多数函数大概只定义一次),这样无论我们使用多少次,我们只支付一次构建价格。

但是嵌套函数呢?每次调用它们的父级时,它们都会被重新定义。这是否意味着我们每次都会重新构造该对象?如果是的话,那不是效率很低吗?私有方法(假设父函数是一个方法)或另一个函数不是更高效吗?我忽略了支持嵌套讨论的范围论点。

我进行了一个简单的实验,似乎支持我上述的论点,因为内部函数版本较慢:

import time

def f(x, y):
    def defined_inside(x, y):
        return x + y
    return defined_inside(x, y)

def defined_outside(x, y):
    return x + y

def g(x, y):
    return defined_outside(x, y)

start = time.time()
for i in range(10000000):
    _ = f(3, 4)
end = time.time()

print("Using inner function it took {} seconds".format(end - start))

start = time.time()
for i in range(10000000):
    _ = g(3, 4)
end = time.time()

print("Using outer function it took {} seconds".format(end - start))

结果:

Using inner function it took 2.494696855545044 seconds
Using outer function it took 1.8862690925598145 seconds

额外问题:如果上述情况属实,这种情况与编译语言(例如 Scala)有何关系?我对嵌套和高阶函数产生了巨大的兴趣,如果这个技巧像看起来那样效率低下,那将是可怕的。

python scala nested
1个回答
0
投票

您可以使用装饰器轻松测试它:

from collections.abc import Callable

def wrapper(fun: Callable[[], None]) -> Callable[[], None]:
    print(f"wrap {fun}")

    def wrap() -> None:
        print(f"calling {fun}")
        fun()

    return wrap

def foo() -> None:

    @wrapper
    def inner() -> None:
        print("inner")

    inner()

foo()
foo()

输出将是:

wrap <function foo.<locals>.inner at 0x1051fbe20>
calling <function foo.<locals>.inner at 0x1051fbe20>
inner
wrap <function foo.<locals>.inner at 0x1051fbec0>
calling <function foo.<locals>.inner at 0x1051fbec0>
inner

每次调用

foo
时,都会重新定义函数
inner
(上面,每个版本都有自己的地址)。这是有道理的,因为您可能想在内部函数中捕获局部变量等。

© www.soinside.com 2019 - 2024. All rights reserved.