为什么 return 列表比附加到 return 变量更快?

Why is returning a list faster than appending to a return variable?

在我看来,使用 return 变量而不是使用 return 语句意味着您应该跳过列表的副本。它的可读性不佳,但我认为它会更快。

所以我测试了一下:

import timeit

TEST_DATA = 10000000
ITERATIONS = 1000

time_taken = timeit.timeit(
"""
def get_big_list(test_data):
    return [random.random() for _ in range(100000)]

return_list = get_big_list(test_data)

""",
setup='import random; test_data ={0}'.format(TEST_DATA), number=ITERATIONS)

print("return_list", time_taken / ITERATIONS)

# ----------------------------- SECOND TEST -----------------------------------

time_taken = timeit.timeit(
"""
def get_big_list(test_data, return_list):
    [return_list.append(random.random()) for _ in range(100000)]

return_list = []
get_big_list(test_data, return_list)

""",
setup='import random; test_data ={0}'.format(TEST_DATA), number=ITERATIONS)

print("return_list", time_taken / ITERATIONS)

我对收到的数字感到非常惊讶,我不明白为什么 return 语句更快。

$ python large_list_return.py 
('return_list', 0.013130356788635254)
('return_list', 0.022573610067367553)
$ python3 large_list_return.py 
return_list 0.016797171516984236
return_list 0.02749005461903289

我发现的另一件有趣的事情是使用 for 循环追加比使用列表理解更快。

time_taken = timeit.timeit(
"""
def get_big_list(test_data, return_list):
    for index in range(100000):
        return_list.append(random.random())

return_list = []
get_big_list(test_data, return_list)

""",
setup='import random; test_data ={0}'.format(TEST_DATA), number=ITERATIONS)

return编辑了这些数字

python large_list_return.py 
('return_list', 0.0133241708278656)
('return_list', 0.019642770051956176)
python3 large_list_return.py 
return_list 0.017075919962022453
return_list 0.024502045304980128

Python 不会制作副本,除非您明确要求副本(例如,通过获取列表的 切片 )——您只是得到另一个引用无论如何创建的列表。

append 另一方面,可能需要获取更多内存并复制以前的内容——它的摊销时间为 O(1),但仍然需要更多工作。

此外,由于您对应该是循环的内容使用了列表理解:

[return_list.append(random.random()) for _ in range(100000)]

在您的第二个实现中,您 创建了另一个列表(然后您将其丢弃),一个由 None 的十万次出现组成(来自 append 的 return 值。这就是 for 在那里更快的原因——它避免了无用地创建冗余列表。

如果您正在寻找优化,请考虑 提升 命名空间查找,正如我在上面的评论中提到的那样。具体来说,使用:

def get_big_list(test_data):
    r = random.random
    return [r() for _ in range(100000)]

在我过时的笔记本电脑上,这需要 12 毫秒,而您的第一个版本是 17 毫秒(这告诉我们每个 random.random 查找大约需要 50 纳秒)。