我认为当我替换此代码时,我会提高性能:
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中),并且是不可变的。然后代码对象似乎使用全局环境来实现闭包。这似乎表明我希望的优化不会发生。
答案 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在尝试访问“全局”变量/函数时会受到性能损失。如果你可以在本地进行访问,你可以节省一点时间。