使用python csv模块,为什么创建相同值的列表会加快行列表的创建?

使用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秒)): 我能理解为

我正在使用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秒)):

我能理解为什么
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})