名称查找在 Python 中相对昂贵,因此对于大型循环,您可以通过更改类似的内容来获得性能优势
for element in my_list:
do_something(element)
类似于
ds = do_something
for element in my_list:
ds(element)
这样解释器只需要在每次迭代时查找一个局部变量。如果列表推导式调用该函数,此优化是否也有用?
请您参考如下方法:
是的。我使用 timeit 设置了一个简单示例:
from __future__ import print_function
import timeit
setup = '''
def return_five():
return 6
def f1():
return [return_five() for i in range(10000)]
def f2():
r = return_five
return [r() for i in range(10000)]
'''
print('Not a local variable:')
print(min(timeit.Timer('a = f1()', setup=setup).repeat(7, 1000)))
print('Local variable:')
print(min(timeit.Timer('a = f2()', setup=setup).repeat(7, 1000)))
这个脚本的典型结果是
Not a local variable:
1.22310686111
Local variable:
1.17974805832
我惊讶地发现第二个函数 f2 始终比第一个函数快 3.5%。我原以为 Python 在遇到 f1 中的列表理解时只会查找一次 return_five,但它似乎每次都查找它。
(大概是这样,如果所涉及的函数之一具有以某种方式更改名称查找的副作用,则列表理解会正确运行。对我来说,这似乎是一个非常病态的副作用,但我想最好稍微比引入可能导致难以追踪错误的优化效率低。)
当我将列表理解的大小从 10,000 更改为 10 时,差距缩小了,因此 f2 仅比 f1 快 0.8%。这证实了在循环的每次迭代中都执行了函数名称查找。
