我以为替换以下代码可以提高性能:

def f(a, b):
  return math.sqrt(a) * b
result = []
a = 100
for b in range(1000000):
  result.append(f(a, b))

和:
def g(a):
  def f(b):
    return math.sqrt(a) * b
  return f
result = []
a = 100
func = g(a)
for b in range(1000000):
  result.append(func(b))

我假设由于执行关闭时a是固定的,所以解释器将预先计算涉及a的所有内容,因此math.sqrt(a)将重复一次,而不是重复1000000次。

我的理解是否总是正确的,或者总是不正确的,或者是正确/不正确的,取决于实现方式?

我注意到func的代码对象是在运行时之前构建的(至少在CPython中),并且是不可变的。然后,该代码对象似乎使用全局环境来实现关闭。这似乎表明我希望进行的优化没有发生。

最佳答案



这个假设是错误的,我不知道它是从哪里来的。闭包仅捕获变量绑定(bind),在您的情况下,它捕获a的值,但这并不意味着发生了更多的魔术操作:每次调用math.sqrt(a)时,表达式f仍将求值。

毕竟,每次都必须计算它,因为解释器不知道sqrt是“纯”的(返回值仅取决于参数,并且没有副作用)。像您期望的那样的优化在函数式语言中是可行的(引用透明性和静态类型在这里有很大帮助),但是很难在Python中实现,这是一种强制性的动态类型的语言。

就是说,如果您想预先计算math.sqrt(a)的值,则需要明确地执行以下操作:

def g(a):
  s = math.sqrt(a)
  def f(b):
    return s * b
  return f

或使用lambda:
def g(a):
  s = math.sqrt(a)
  return lambda b: s * b

现在g实际上返回了一个带有1个参数的函数,您只需要用一个参数调用结果即可。

10-07 11:58
查看更多