为什么连接字符串比连接它们更快?

时间:2013-11-25 06:43:57

标签: python string loops memory python-3.3

据我所知,“。join(iterable_of_strings)是连接字符串的首选方法,因为它允许进行优化,避免必须将不可变对象重写到内存的次数超过必要时间。

在表达式中添加字符串可靠地运行得比为它加入适度大量操作的速度更快。

我加入了大约2.9-3.2秒的时间,并且在我的笔记本电脑上使用Python 3.3添加了运行此代码的2.3-2.7。谷歌搜索这个我找不到一个好的答案。有人可以解释可能发生的事情或指导我找到一个好的资源吗?

import uuid
import time

class mock:
    def __init__(self):
        self.name = "foo"
        self.address = "address"
        self.age = "age"
        self.primarykey = uuid.uuid4()

data_list = [mock() for x in range(2000000)]

def added():
    my_dict_list = {}
    t = time.time()
    new_dict = { item.primarykey: item.name + item.address + item.age for item in data_list }
    print(time.time() - t)

def joined():
    my_dict_list = {}
    t = time.time()
    new_dict = { item.primarykey: ''.join([item.name, item.address, item.age]) for item in data_list }
    print(time.time() - t)

joined()
added()

2 个答案:

答案 0 :(得分:5)

  

据我所知,“。join(iterable_of_strings)是连接字符串的首选方法,因为它允许进行优化,避免必须将不可变对象重写到内存的次数超过必要时间。

你理解有些不正确。 "".join(iterable_of_strings)是连接可迭代字符串的首选方法,原因在于您解释过。

但是,您没有可迭代的字符串。你只有三个字符串。连接三个字符串的最快方法是将它们与+一起添加,或使用.format()%。这是因为在你的情况下你必须首先创建iterable,然后加入字符串,以避免复制一些非常小的字符串。

.join()在你有这么多字符串之前不会变得更快,这使得愚蠢的代码无论如何都要使用其他方法。当发生这种情况取决于你的Python实现,版本和字符串的长度,但我们通常谈论的是十多个字符串。

虽然并非所有实现都具有快速连接,但我已经测试了CPython,PyPy和Jython,并且所有这些都只有几个字符串更快或更快地连接。

从本质上讲,您应该使用+.join()之间的选择,具体取决于代码清晰度,直到您的代码运行。然后,如果您关心速度:对您的代码进行配置和基准测试。不要坐下来猜测。

一些时间安排:http://slides.colliberty.com/DjangoConEU-2013/#/step-40

视频说明:http://youtu.be/50OIO9ONmks?t=18m30s

答案 1 :(得分:3)

您看到的时差来自于创建要传递给join的列表。虽然你可以通过使用元组获得一个小的加速,但是当只有几个短字符串时,它仍然比仅与+连接更慢。

如果你有一个可迭代的字符串开头,而不是一个字符串作为属性的对象,那将会有所不同。然后你可以直接在iterable上调用join,而不需要为每个调用构建一个新的。{/ p>

以下是我对timeit模块进行的一些测试:

import timeit

short_strings = ["foo", "bar", "baz"]
long_strings = [s*1000 for s in short_strings]

def concat(a, b, c):
    return a + b + c

def concat_from_list(lst):
    return lst[0] + lst[1] + lst[2]

def join(a, b, c):
    return "".join([a, b, c])

def join_tuple(a, b, c):
    return "".join((a, b, c))

def join_from_list(lst):
    return "".join(lst)

def test():
    print("Short strings")
    print("{:20}{}".format("concat:",
                           timeit.timeit(lambda: concat(*short_strings))))
    print("{:20}{}".format("concat_from_list:",
                           timeit.timeit(lambda: concat_from_list(short_strings))))
    print("{:20}{}".format("join:",
                           timeit.timeit(lambda: join(*short_strings))))
    print("{:20}{}".format("join_tuple:",
                           timeit.timeit(lambda: join_tuple(*short_strings))))
    print("{:20}{}\n".format("join_from_list:",
                             timeit.timeit(lambda: join_from_list(short_strings))))
    print("Long Strings")
    print("{:20}{}".format("concat:",
                           timeit.timeit(lambda: concat(*long_strings))))
    print("{:20}{}".format("concat_from_list:",
                           timeit.timeit(lambda: concat_from_list(long_strings))))
    print("{:20}{}".format("join:",
                           timeit.timeit(lambda: join(*long_strings))))
    print("{:20}{}".format("join_tuple:",
                           timeit.timeit(lambda: join_tuple(*long_strings))))
    print("{:20}{}".format("join_from_list:",
                           timeit.timeit(lambda: join_from_list(long_strings))))

输出:

Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>> 
>>> test()
Short strings
concat:             0.5453461176251436
concat_from_list:   0.5185697357936024
join:               0.7099379456477868
join_tuple:         0.5900842397209949
join_from_list:     0.4177281794285359

Long Strings
concat:             2.002303591571888
concat_from_list:   1.8898819841869416
join:               1.5672863477837913
join_tuple:         1.4343144915087596
join_from_list:     1.231374639083505

因此,从现有列表加入总是最快的。如果单个项目很短,则与+连接的速度会更快,但对于长串,它总是最慢的。我怀疑concatconcat_from_list之间的区别来自于测试代码中函数调用中列表的解包。