功能关闭性能

时间:2012-04-02 00:02:45

标签: python performance python-3.x closures implementation

我认为当我替换此代码时,我会提高性能:

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中),并且是不可变的。然后代码对象似乎使用全局环境来实现闭包。这似乎表明我希望的优化不会发生。

3 个答案:

答案 0 :(得分:13)

  

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

这个假设是错误的,我不知道它来自哪里。闭包只捕获变量绑定,在你的情况下它捕获a的值,但这并不意味着还有更多的魔法:每次{{1}仍然会计算表达式math.sqrt(a)调用。

毕竟,每次都计算 ,因为解释器不知道f是“纯”的(返回值只取决于参数而没有任何一方 - 执行效果)。像你期望的那样优化在函数式语言中是实用的(引用透明性和静态类型在这里有很多帮助),但在Python中很难实现,这是一种命令式和动态类型的语言。

也就是说,如果你想预先计算sqrt的值,你需要明确地这样做:

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 实际上返回一个带有1个参数的函数,你必须只用一个参数调用结果。

答案 1 :(得分:3)

代码不会静态评估;每次仍然计算函数内部的代码。函数对象包含表示函数中代码的所有字节代码;它没有评估任何一个。您可以通过计算一次昂贵的价值来改善问题:

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

当然,在这个简单的例子中,你可以更多地提高性能:

a = 100
root_a = math.sqrt(a)
result = [root_a * b for b in range(1000000)]

但我认为你正在处理一个比不能扩展的更复杂的例子?

答案 2 :(得分:1)

像往常一样,timeit模块是你的朋友。尝试一些事情,看看它是怎么回事。如果你不关心编写丑陋的代码,这可能会有所帮助:

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

显然,python在尝试访问“全局”变量/函数时会受到性能损失。如果你可以在本地进行访问,你可以节省一点时间。