为什么返回列表的速度要快于附加到返回变量?

时间:2015-02-04 02:18:37

标签: python python-3.x

在我看来,使用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)

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

$ 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)

返回这些数字

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

1 个答案:

答案 0 :(得分:3)

除非您明确要求副本(例如,通过获取列表的切片),否则Python不会制作副本 - 您只需获得对已创建的列表的另一个引用。

另一方面,

append可能需要获得更多内存并复制以前的内容 - 它是分摊的O(1),但它仍然需要做更多的工作。

此外,因为你使用列表推导来应该是一个循环:

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

在你的第二个实现中,你创建另一个列表(然后你就扔掉了),一个组成了十万个None(返回值来自append。这就是for更快的原因 - 它避免无用地创建冗余列表。

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

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

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