我得到的结果类似,但不像你的那么戏剧性。(注意使用timeit
模块来计时代码执行,并注意我已经将列表创建分解出来,因为它对两个测试用例都是共同的。)
import csv
from timeit import Timer
def write_csv(f, n):
"""Write n records to the file named f."""
w = csv.writer(open(f, 'wb'))
for i in xrange(n):
w.writerow((i, "squared", "equals", i**2))
def test1(rows, f, n):
for i, r in enumerate(csv.reader(open(f))):
rows[i] = r
def test2(rows, f, n):
for i, r in enumerate(csv.reader(open(f))):
pass
def test(t):
return (Timer('test%d(rows, F, N)' % t,
'from __main__ import test%d, F, N; rows = [None] * N' % t)
.timeit(number=1))
>>> N = 1446311
>>> F = "test.csv"
>>> write_csv(F, N)
>>> test(1)
2.2321770191192627
>>> test(2)
1.7048690319061279
以下是我的猜测,关于正在发生的事情。在两个测试中,CSV读取器从文件中读取记录,并创建代表该记录的内存数据结构。
在test2中,由于记录未被存储,因此数据结构会立即被删除(在循环的下一次迭代中,row变量被更新,因此上一个记录的引用计数将被减少,所以内存将被回收)。这使得先前记录使用的内存可供重复使用:此内存已在计算机的虚拟内存表中,并且可能仍在缓存中,因此速度较快。
在test1中,由于记录被存储,每个记录都必须在新的内存区域中分配,该区域必须由操作系统进行分配并复制到缓存中,因此速度相对较慢。
因此,时间不是由列表赋值占用的,而是由内存分配占用的。
以下是另外两个测试,说明了正在发生的情况,没有csv模块的复杂因素。在test3中,我们为每行创建一个新的100元素列表,并将其存储。在test4中,我们为每行创建一个新的100元素列表,但我们不存储它,我们将其丢弃,以便内存可以在下一次循环时重复使用。
def test3(rows, f, n):
for i in xrange(n):
rows[i] = [i] * 100
def test4(rows, f, n):
for i in xrange(n):
temp = [i] * 100
rows[i] = None
>>> test(3)
9.2103338241577148
>>> test(4)
1.5666921138763428
所以我的观点是,如果不需要同时将所有行存储在内存中,请不要这样做!如果可以的话,逐个读取它们,逐个处理它们,然后忘记它们,这样Python就可以对它们进行解除分配。