4

csv我正在使用Python中的无价模块读取一个大型 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 秒):

r = csv.reader(open("data.csv", "rb"), delimiter=";")
b = [row for row in r]

我可以理解为什么在创建之后b几乎不需要任何时间a。但我会(天真地)认为,第二个代码块,其中 onlyb是 created 而不是a,将是更快的脚本。冒着出现非 Pythonic 的风险,我很想知道是否有人可以解释为什么创建a然后几乎是单独b创建的两倍。b

此外,如果这种速度提升在更复杂的操作中是一致的,是否有充分的理由(除了样式/可读性问题)利用它?比我更精明的 Python 程序员是否已经使用我从未听说过的一些传统方法节省了相同的时间?

如果我a使用整数而不是构造None,我会得到相同的结果。如果不是迭代csv.reader我迭代的对象open("data.csv", "rb").readlines(),那么时间就像我期望的那样:b单​​独创建比创建athen更快b。因此,时间差异可能与csv.reader对象的属性或类似它的更一般类的对象的属性有关。如果我b在之前创建,则时间与我单独 a创建的时间大致相同。b

一些注意事项:

  • b在此之前创建与单独创建a所需的时间相同。b
  • 我没有在交互模式下逐行运行这些。我将每个都作为单独的脚本运行。
  • 我并没有真正尝试创建一个列表,其中包含与 相同长度r的列表,或者r.
  • 万一这很重要,我正在 64 位 Windows 7 上使用 Enthought Python 发行版 7.3-2 运行 Python 2.7.3。
4

2 回答 2

6

你看过b你的第一个例子吗?它是空的,因为r第一个列表理解已经筋疲力尽了。所有行都已被迭代,并且 - 正如@soulcheck 指出的那样 - 创建一个包含 400 万个Nones 的列表比包含 400 万个子列表的列表要快得多。

于 2013-04-06T20:58:54.423 回答
3

这可能会提供一些见解。让我们以 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不保留对 的任何引用row,它将立即被垃圾收集,打开该内存以供重用。如果我们坚持下去,自然地,我们将不得不为每个新列表分配内存。

于 2013-04-06T21:28:38.670 回答