Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/350.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
列表在python中的性能是否很差?_Python - Fatal编程技术网

列表在python中的性能是否很差?

列表在python中的性能是否很差?,python,Python,我试图从某个大文件中读取数据并将其写回,但我意识到,主要成本来自将数据分配到列表,而不是从文件中读取或写入数据 rows = [None] * 1446311 begin = datetime.datetime.now() for i in range( 1446311 ): row = csvReader.next() rows[i] = row print datetime.datetime.now() - begin 上面的代码

我试图从某个大文件中读取数据并将其写回,但我意识到,主要成本来自将数据分配到列表,而不是从文件中读取或写入数据

    rows = [None] * 1446311
    begin = datetime.datetime.now()
    for i in range( 1446311 ):
       row = csvReader.next()
       rows[i] = row
    print datetime.datetime.now() - begin
上面的代码需要18秒,但如果我注释掉第5行(
rows[I]=row
),则需要5秒,我已经提前构建了列表(即保留内存),但为什么它仍然如此缓慢?我能做些什么来加快速度?我在csvReader中尝试了
逐行操作
,但它的性能更差

问候,,
约翰

我得到了类似的结果,但没有你的那么戏剧性。(注意使用模块来计时代码执行,并且注意我已经考虑了列表创建,因为它对两个测试用例都是通用的。)

这是我对正在发生的事情的猜测。在这两个测试中,CSV阅读器从文件中读取一条记录,并在内存中创建一个表示该记录的数据结构

test2
中,如果没有存储记录,数据结构或多或少会立即被删除(在循环的下一次迭代中,
变量会被更新,因此前一条记录的引用计数会减少,从而回收内存)。这使得用于上一条记录的内存可以重用:该内存已经在计算机的虚拟内存表中,并且可能仍在缓存中,因此它(相对)比较快

在存储记录的
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可以取消分配它们。

编辑:第一部分不是很有效(请参阅下面的注释)

您是否尝试过这样做:

rows = [None] * 1446311
for i in range( 1446311 ):
   rows[i] = csvReader.next()
rows = []
for i in range( 1446311 ):
   rows.append(csvReader.next())
因为从我在代码中看到的情况来看,您复制了两次数据:一次是使用
行=…
从文件复制到内存,一次是从
复制到
行[I]
。因为这里有不可变的东西(字符串),所以我们实际上讨论的是数据的拷贝,而不是引用的拷贝

此外,即使您以前创建了一个空列表,您也会将一大块数据放入其中;由于您仅在开始时放入
None
,因此未保留任何实际内存空间。也许你可以直接写一个非常简单的东西如下:

rows = [None] * 1446311
for i in range( 1446311 ):
   rows[i] = csvReader.next()
rows = []
for i in range( 1446311 ):
   rows.append(csvReader.next())
或者甚至可以直接使用生成器语法

rows = list(csvReader)

编辑 在阅读了加雷思的答案后,我对我的建议做了一些测试。顺便说一句,在从迭代器读取数据时,请注意采取一些保护措施,以便在迭代器比预期短时能够很好地停止:

>>> from timeit import Timer
>>> import csv
>>> # building some timing framework:
>>> def test(n):
    return min(Timer('test%d(F, N)' % t,
                  'from __main__ import test%d, F, N' % t)
            .repeat(repeat=10, number=1))

>>> F = r"some\big\csvfile.csv"
>>> N = 200000
>>> def test1(file_in, number_of_lines):
    csvReader = csv.reader(open(file_in, 'rb'))
    rows = [None] * number_of_lines
    for i, c in enumerate(csvReader):  # using iterator syntax
        if i > number_of_lines:  # and limiting the number of lines
            break
        row = c
        rows[i] = row
    return rows

>>> test(1)
0.31833305864660133

>>> def test2(file_in, number_of_lines):
    csvReader = csv.reader(open(file_in, 'rb'))
    rows = [None] * number_of_lines
    for i, c in enumerate(csvReader):
        if i > number_of_lines:
            break
        row = c
    return rows

>>> test(2)
0.25134269758603978  # remember that only last line is stored!

>>> def test3(file_in, number_of_lines):
    csvReader = csv.reader(open(file_in, 'rb'))
    rows = [None] * number_of_lines
    for i, c in enumerate(csvReader):
        if i > number_of_lines:
            break
        rows[i] = c
    return rows

>>> test(3)
0.30860502255637812

>>> def test4(file_in, number_of_lines):
    csvReader = csv.reader(open(file_in, 'rb'))
    rows = []
    for i, c in enumerate(csvReader):
        if i > number_of_lines:
            break
        rows.append(c)
    return rows

>>> test(4)
0.32001576256431008

>>> def test5(file_in, number_of_lines):
    csvReader = csv.reader(open(file_in, 'rb'))
    rows = list(csvReader)  
    # problem: there's no way to limit the number of lines to parse!
    return rows

>>> test(5)
0.30347613834584308
我们可以看到,对于大于文档中行数的N,时间上没有很大的差异<代码>测试2在我的机器上,毫无疑问只是有一点不同
test5更优雅,但不能限制解析的行数,这可能会让人恼火


因此,如果您同时需要所有行,我的建议是使用最优雅的解决方案,即使稍微长一点:
test4
。但是,正如Gareth所问,您可能不需要一次完成所有任务,这是获得速度和内存的最佳方式。

我认为L5存在与不存在在运行时没有太大区别。(不过,我不得不伪造csvReader.next()调用,这可能会产生影响。)正如Gareth解释的那样,您没有为所有实际行预分配内存,而这种分配正是需要时间的。如果您不要求所有行同时在内存中,那么通过使用生成器/生成器表达式来构造代码,您可能会获得性能提高。您是否尝试过这些建议中的任何一个,以查看它们是否对运行时间产生了任何影响?我是在阅读Gareth的回答后这样做的。请看我答案中的编辑。@Gareth哦,对不起,我错过了是你要求更多的材料。很抱歉回答中的延迟,我花了时间格式化。在顶部附近仍然有几个错误:“您复制了两次数据”以及一些关于可变对象和不可变对象的混淆。否则,看起来不错。哦,我明白了:我没有想到,
csv
模块会为每一行返回一个值列表。所以这是正确的,我的答案的开头有点离题。顺便问一下,
test1
是否会返回一个引用列表,所有引用都指向csvReader解析的最后一个
列表?您提到的时间是从内存分配中提取的,您是指列表内容的内存还是列表的引用(指针)?test3和test4都只为列表内容分配内存,即我认为[i]*100一次?我指的是所有行的内存。此外,测试3和4每次循环分配一个新的100元素列表。而且,您还说下一个循环可以利用我们从上一个循环分配的内存,为什么这些内存会不同于其他“野生”但可用的内存块?因为我们已经取消引用了以前分配的内存,对吗?取消分配的内存与“野生”(从未分配)内存不同!“野生”内存需要由操作系统(页表等)分配,然后缓存;最近释放的内存没有。是的,我错过了,它仍然更快,即使下一个循环所需的内存大小可能更大