Python中对List和Generator进行迭代的速度比较



在比较Python Generators与List的使用以获得更好的性能/优化时,我读到Generators的创建速度比List快,但在列表上迭代比generator快。但我编写了一个例子,用小样本和大样本数据对其进行测试,结果相互矛盾。

当我测试在生成器上迭代的速度并使用1_000_000_0000列出时,实际生成器将有500000000个数字。我看到了Generator迭代比列表更快的结果

from time import time
my_generator = (i for i in range(1_000_000_000) if i % 2 == 0)
start = time()
for i in my_generator:
    pass
print("Time for Generator iteration - ", time() - start)
my_list = [i for i in range(1_000_000_000) if i % 2 == 0]
start = time()
for i in my_list:
    pass
print("Time for List iteration - ", time() - start)

输出为:

Time for Generator iteration -  67.49345350265503
Time for List iteration - 89.21837282180786

但是如果我在输入中使用小块数据10_000_0000而不是1_000_000_000,则List迭代比Generator更快。

from time import time
my_generator = (i for i in range(10_000_000) if i % 2 == 0)
start = time()
for i in my_generator:
    pass
print("Time for Generator iteration - ", time() - start)
my_list = [i for i in range(10_000_000) if i % 2 == 0]
start = time()
for i in my_list:
    pass
print("Time for list iteration - ", time() - start)

输出为:

Time for Generator iteration -  1.0233261585235596
Time for list iteration -  0.11701655387878418

为什么会发生这种行为?

在理解了@gimix和@Dani Mesejo的观点后,我找到了答案。事实上,列表迭代比生成器迭代更快

在生成器的情况下,对生成器的调用就像对每次迭代的函数调用一样——我们也对每次迭代调用提醒操作(模数(,因为它会使每次调用的速度更慢。。。而在列表的情况下,它是在创建过程中计算的,迭代更快。因此,列表的创建可能比生成器的创建慢,但列表的迭代肯定比列表快

上面的代码使用time模块,这是不可靠的!!现在,我对1_000_000和1_000_000_0000数据使用了timeit,在这两种情况下,列表迭代都更快:

import timeit
mysetup = '''my_generator = (i for i in range(10_000_000) if i % 2 == 0)
'''
mycode = '''
for i in my_generator:
    pass
'''
mysetup1 = '''my_list = [i for i in range(10_000_000) if i % 2 == 0]'''
mycode1 = '''
for i in my_list:
    pass
'''
print (timeit.timeit(setup = mysetup,
                    stmt = mycode,
                     number = 1))
print (timeit.timeit(setup = mysetup1,
                    stmt = mycode1,
                     number = 1))

以更好地了解发电机在效率方面的好处。假设您想要读取一个具有10M行的文件。首先,你用下面这样的常规方法阅读:

from time import time
first_ts = time()
def regular_file_reader(filename):
    file_ = open(filename, "r")
    data = file_.readlines()
    file_.close()
    return data
for row in regular_file_reader("sample_file.csv"):
    print(row)
    global second_time
    second_time = time()
    break
print(second_time - first_ts)

正如您在从循环中读取我们break编辑的文件的第一行后所看到的,因为这就是生成器的区别;仅读取第一元素";。对于迭代下一个,它甚至可能是低效的。

def generator_file_reader(filename):
    with open(filename, "r") as f:
        for row in f:
            yield row

for row in generator_file_reader("sample_file.csv"):
    print(row)
    global second_time
    second_time = time()
    break
print(second_time - first_ts)

在这种情况下,由于生成器只读取第一行,而不是整个文件,因此使用生成器要快得多。

最新更新