使用python csv模块,为什么创建相同值的列表会加快行列表的创建?
我正在使用Python中非常宝贵的使用python csv模块,为什么创建相同值的列表会加快行列表的创建?,python,csv,Python,Csv,我正在使用Python中非常宝贵的csv模块读取一个大型csv文件(超过400万行)。在对各种方法进行计时时,我遇到了一个不直观的结果 如果我运行以下脚本,大约需要11-12秒b几乎在a之后立即创建 r = csv.reader(open("data.csv", "rb"), delimiter=";") a = [None for row in r] b = [row for row in r] 但是,如果我运行一个根本不创建a的类似脚本,代码将花费更长的时间(21-22秒)): 我能理解为
csv
模块读取一个大型csv文件(超过400万行)。在对各种方法进行计时时,我遇到了一个不直观的结果
如果我运行以下脚本,大约需要11-12秒<代码>b几乎在a
之后立即创建
r = csv.reader(open("data.csv", "rb"), delimiter=";")
a = [None for row in r]
b = [row for row in r]
但是,如果我运行一个根本不创建a
的类似脚本,代码将花费更长的时间(21-22秒)):
我能理解为什么b
的创建在a
已经创建之后几乎不需要时间。但我(天真地)认为,第二个代码块(其中只创建了b
,而不是a
)将是更快的脚本。冒着看起来不像蟒蛇的风险,我想知道是否有人能解释为什么创建a
然后创建b
的速度几乎是单独创建b
的两倍。
此外,如果这种速度提升在更复杂的操作中是一致的,是否有充分的理由(除了样式/可读性问题)不利用它?比我更精明的Python程序员是否已经通过一些我从未听说过的常规方法实现了同样的时间节约
如果我用一个整数而不是None
来构造a
,我会得到相同的结果。如果我不是在csv.reader
对象上迭代,而是在open(“data.csv”,“rb”).readlines()
上迭代,那么时间安排与我预期的一样:单独创建b
比创建a
然后创建b
要快。因此,时间差异可能与csv.reader
对象的属性有关,或者与类似对象的更一般类的属性有关。如果我在a
之前创建b
,时间与我单独创建b
的时间大致相同
一些注意事项:
- 在
之前创建a
与单独创建b
所需时间相同b
- 我不是在交互模式下逐行运行这些。我将每个脚本作为单独的脚本运行李>
- 我并不是真的试图创建一个包含与
长度相同的行的列表,或者r
中的行的列表r
- 如果有必要的话,我正在64位Windows7上运行Python2.7.3,使用Enthound Python发行版7.3-2
b
?它是空的,因为r
已被第一个列表耗尽。所有行都已经被迭代过了,正如@soulcheck所指出的,创建一个400万None
s的列表要比创建一个包含400万个子列表的列表快得多。这可能会给我们一些启示。让我们以10行的CSV文件为例,比较一下:
import csv
from collections import Counter
r = csv.reader(open('foo.csv'))
a = [id(row) for row in r]
r = csv.reader(open('foo.csv'))
b = [row for row in r]
b_id = [id(row) for row in b]
c1 = Counter(a)
c2 = Counter(b_id)
print c1
print c2
这导致
Counter({139713821424456: 5, 139713821196512: 5})
Counter({139713821196512: 1, 139713821669136: 1, 139713821668776: 1, 139713821196584: 1, 139713821669064: 1, 139713821668560: 1, 139713821658792: 1, 139713821668704: 1, 139713821668848: 1, 139713821668632: 1})
换句话说,在
a
中,我们反复使用相同的内存。由于a
的列表理解没有保留对行的任何引用,因此它将立即被垃圾收集,并打开该内存以供重用。如果我们保留它,自然,我们将不得不为每个新列表分配内存。我认为这并不能回答这个问题,这就是为什么a
创建得更快。@LevLevitsky你的意思是为什么400万个非的列表比包含400万行对象的列表创建得更快?@soulcheck是的,这就是问题所在。对我来说,这并不是很直观,为什么它要快得多:不是在这两种情况下都要计算行吗?我猜是的,唯一的区别是所有None(或小int)共享同一个指针。不过,我很想在你的回答中看到一个解释,我的猜测可能会大错特错,因为我对实现知之甚少。@Levleivsky:我也不知道实现的细节,但我认为这与内存分配有关——将400万行读取到RAM中(并将它们全部存储起来)Python解释器可能比“覆盖”相同的行花费的时间要长400万次。但这是一个有趣的问题。关于记忆的观点很好。可能有一些malloc
正在进行。在Unix上,您可以查看进程的执行时间;大量额外的“系统时间”意味着延迟是由于系统调用(如内存分配)造成的,而额外的用户时间意味着时间只是用来移动数据。
Counter({139713821424456: 5, 139713821196512: 5})
Counter({139713821196512: 1, 139713821669136: 1, 139713821668776: 1, 139713821196584: 1, 139713821669064: 1, 139713821668560: 1, 139713821658792: 1, 139713821668704: 1, 139713821668848: 1, 139713821668632: 1})