串联字符串比附加到列表更快

时间:2015-10-03 09:29:19

标签: python string performance list python-3.x

我正在尝试隔离列表中的特定项目(例如[0, 1, 1]将返回[0, 1])。我设法通过这个,但我注意到一些奇怪的事情。

当我尝试追加到列表时,它比我连接字符串然后将其拆分时慢了大约7倍。

这是我的代码:

import time
start = time.time()

first = [x for x in range(99999) if x % 2 == 0]
second = [x for x in range(99999) if x % 4 == 0]

values = first + second

distinct_string = ""

for i in values:
    if not str(i) in distinct_string:
        distinct_string += str(i) + " "

print(distinct_string.split())

print(" --- %s sec --- " % (start - time.time()))

此结果大约在5秒后结束...... 现在列出了这些结果:

import time
start = time.time()

first = [x for x in range(99999) if x % 2 == 0]
second = [x for x in range(99999) if x % 4 == 0]

values = first + second

distinct_list = []

for i in values:
    if not i in distinct_list:
        distinct_list.append(i)

print(distinct_list)

print(" --- %s sec --- " % (start - time.time()))

大约40秒运行。

即使我将大量值转换为字符串,是什么让字符串更快?

3 个答案:

答案 0 :(得分:2)

请注意,通常最好使用timeit来比较函数,这些函数会多次运行相同的函数以获得平均性能,并将重复的代码分解为重点关注重要的性能。这是我的测试脚本:

first = [x for x in range(999) if x % 2 == 0]
second = [x for x in range(999) if x % 4 == 0]

values = first + second

def str_method(values):
    distinct_string = ""
    for i in values:
        if not str(i) in distinct_string:
            distinct_string += str(i) + " "
    return [int(s) for s in distinct_string.split()]

def list_method(values):
    distinct_list = []
    for i in values:
        if not i in distinct_list:
            distinct_list.append(i)
    return distinct_list

def set_method(values):
    seen = set()
    return [val for val in values if val not in seen and seen.add(val) is None]

if __name__ == '__main__':
    assert str_method(values) == list_method(values) == set_method(values)
    import timeit
    funcs = [func.__name__ for func in (str_method, list_method, set_method)]
    setup = 'from __main__ import {}, values'.format(', '.join(funcs))
    for func in funcs:
        print(func)
        print(timeit.timeit(
            '{}(values)'.format(func),
            setup=setup,
            number=1000
        ))

我添加了int转换以确保函数返回相同的内容,并获得以下结果:

str_method
1.1685157899992191
list_method
2.6124089090008056
set_method
0.09523714500392089

请注意,如果您必须转换输入,那么在列表中搜索比在字符串中搜索更快是正确的:

>>> timeit.timeit('1 in l', setup='l = [9, 8, 7, 6, 5, 4, 3, 2, 1]')
0.15300405000016326
>>> timeit.timeit('str(1) in s', setup='s = "9 8 7 6 5 4 3 2 1"')
0.23205067300295923

重复append到列表不是很有效,因为这意味着频繁调整基础对象的大小 - 列表理解,如set版本所示,效率更高。

答案 1 :(得分:1)

在字符串中搜索:

如果不是str(i)在distinct_string中:

快得多

然后在列表中搜索<​​/ p>

如果不是我在distinct_list中:

here are lprofile lines for string search in OP code

Line #      Hits         Time  Per Hit   % Time      Line Contents 


    17     75000     80366013   1071.5     92.7       if not str(i) in distinct_string:
    18     50000      2473212     49.5      2.9                  distinct_string += str(i) + " "

and for list search in OP code

   39     75000    769795432  10263.9     99.1          if not i in distinct_list:
   40     50000      2813804     56.3      0.4              distinct_list.append(i)

答案 2 :(得分:1)

我认为有一个逻辑缺陷使字符串方法看起来更快 匹配长字符串中的子字符串时,in运算符将在包含搜索项的第一个子字符串处提前返回。为了证明这一点,我让循环从最高值向下运行到最小值,它只返回原始循环值的50%(我只检查了结果的长度)。如果匹配是精确的,那么从开始或结束检查序列应该没有区别。我得出结论,字符串方法通过在长字符串的开头附近匹配来缩短很多比较。遗憾的是,重复的特殊选择掩盖了这一点。

在第二次测试中,我让字符串方法搜索" " + str(i) + " "以消除子字符串匹配。现在它的运行速度只比list方法快2倍(但仍然更快)。

@jonrsharpe:关于set_method,我看不出为什么会逐个触摸所有的set元素,而不是像这样的set语句:

def set_method(values):
    return list(set(values))

这会产生完全相同的输出,并且在我的电脑上运行速度提高约2.5倍。