string
无法更改,我想知道如何更有效地连接字符串?我可以这样写: >或类似这样:
s += stringfromelsewhere
写这个问题的时候,我发现一篇很好的文章谈到了这个话题。 .skymind.com /〜ocrow / python_string /
但是在Python 2.x中,所以问题是在Python 3中会有所变化吗?
#1 楼
将字符串附加到字符串变量的最佳方法是使用+
或+=
。这是因为它可读且快速。它们的速度也一样快,您选择的是一个品味问题,后者是最常见的。以下是使用timeit
模块的时间:与扩展字符串相比,列表可能非常快。在某些情况下,这可能是正确的。例如,这里是一个字符的字符串的一百万个追加,首先是字符串,然后是列表:
可以看出,即使生成的字符串长了一百万个字符,附加速度仍然更快。
现在让我们尝试将十千个字符长的字符串附加十万次:
因此,结束字符串的长度约为100MB。那太慢了,追加到列表上要快得多。该时间不包括最终的
a.join()
。那要花多长时间? a = a + b:
0.11338996887207031
a += b:
0.11040496826171875
糟糕。事实证明,即使在这种情况下,append / join的速度也较慢。 Python 2吗?那是100MB的内存?)
但是真正的关键是Python 2.3。我什至不告诉您时间安排,因为它是如此之慢以至于还没有完成。这些测试突然需要几分钟。除了append / join之外,它与在以后的Python中一样快。
对。在石器时代,字符串连接在Python中非常缓慢。但是在2.4上已经不存在了(或者至少是Python 2.4.7),因此在2008年Python 2.3停止更新时,使用append / join的建议已过时,您应该已经停止使用它。 :-)
(更新:当我更仔细地进行测试时,结果发现在Python 2.3上使用
+
和+=
对于两个字符串也更快。建议使用''.join()
一定是一个误解)但是,这是CPython。其他实现可能还有其他问题。这是过早优化是万恶之源的又一个原因。除非先进行测量,否则不要使用被认为“更快”的技术。
因此,进行字符串连接的“最佳”版本是使用+或+ =。如果事实证明那对您来说很慢,那是不太可能的,那么请执行其他操作。因为有时它实际上更清晰。尤其是当您应将其串联在一起时,应以空格,逗号或换行符分隔。
评论
如果您有多个字符串(n> 10),“”。join(list_of_strings)仍然会更快
–迈克·奥塔玛(Mikko Ohtamaa)
2012年8月29日在5:34
+ =快速的原因是,如果refcount为1,则cpython中会有一个性能缺陷-它在所有其他python实现中都无法使用(除了配置非常特殊的pypy build之外)
–罗尼
2012年8月29日在6:45
为什么要这么大赞?使用一种仅对一种特定实现有效的算法,并且实质上具有修复二次时间算法的脆弱技巧的更好方法是什么?您也完全误解了“过早的优化是万恶之源”的观点。那句话是谈论小优化。这是从O(n ^ 2)到O(n)的不小的优化。
– Wes
2012年8月31日在2:24
这是实际的报价:“我们应该忘掉效率低下的情况,大约有97%的时间是这样:过早的优化是万恶之源。但是,我们不应该在那3%的临界水平上放弃自己的机会。好的程序员不会由于这种推理而感到沾沾自喜,他应该明智地仔细查看关键代码;但只有在确定了该代码之后,”
– Wes
2012年8月31日在2:28
没有人说a + b慢。当您多次执行a = a + b时,这是二次方的。 a + b + c并不慢,我重复一遍也不慢,因为它只需要遍历每个字符串一次,而它必须使用a = a + b方法多次遍历先前的字符串(假定处于循环中)某种)。请记住,字符串是不可变的。
– Wes
2012年8月31日11:15
#2 楼
如果要串联很多值,那么两者都不是。追加列表很昂贵。您可以为此使用StringIO。尤其是如果您要通过大量操作构建它。from cStringIO import StringIO
# python3: from io import StringIO
buf = StringIO()
buf.write('foo')
buf.write('foo')
buf.write('foo')
buf.getvalue()
# 'foofoofoo'
如果您已经从其他操作中获得了完整的清单,则只需使用
''.join(aList)
从python常见问题解答:将多个字符串连接在一起的最有效方法是什么? >字符串在一起效率低下,因为每个串联都会创建一个新的
对象。在一般情况下,总运行时成本是字符串总长度的二次方。
要累积许多str对象,建议的惯用法是将它们
放入列表中,然后最后调用str.join():
chunks = []
for s in my_strings:
chunks.append(s)
result = ''.join(chunks)
(另一个相当有效的习惯用法是使用io.StringIO)
累积许多bytes对象,建议的惯用法是使用就地串联(+ =运算符)扩展
bytearray对象:
result = bytearray()
for b in my_bytes_objects:
result += b
br />编辑:我很愚蠢,并且将结果向后粘贴,使其看起来比cStringIO更快。我还添加了针对bytearray / str concat的测试,以及第二轮使用较大字符串和较大字符串的测试。 (python 2.7.3)用于大量字符串的ipython测试示例
try:
from cStringIO import StringIO
except:
from io import StringIO
source = ['foo']*1000
%%timeit buf = StringIO()
for i in source:
buf.write(i)
final = buf.getvalue()
# 1000 loops, best of 3: 1.27 ms per loop
%%timeit out = []
for i in source:
out.append(i)
final = ''.join(out)
# 1000 loops, best of 3: 9.89 ms per loop
%%timeit out = bytearray()
for i in source:
out += i
# 10000 loops, best of 3: 98.5 µs per loop
%%timeit out = ""
for i in source:
out += i
# 10000 loops, best of 3: 161 µs per loop
## Repeat the tests with a larger list, containing
## strings that are bigger than the small string caching
## done by the Python
source = ['foo']*1000
# cStringIO
# 10 loops, best of 3: 19.2 ms per loop
# list append and join
# 100 loops, best of 3: 144 ms per loop
# bytearray() +=
# 100 loops, best of 3: 3.8 ms per loop
# str() +=
# 100 loops, best of 3: 5.11 ms per loop
评论
Py3中不存在cStringIO。请改用io.StringIO。
–lvc
2012年8月29日在1:52
至于为什么反复附加到字符串可能会很昂贵:joelonsoftware.com/articles/fog0000000319.html
– Wes
2012年8月29日在1:58
#3 楼
在Python> = 3.6中,新的f字符串是连接字符串的有效方法。>>> name = 'some_name'
>>> number = 123
>>>
>>> f'Name is {name} and the number is {number}.'
'Name is some_name and the number is 123.'
评论
如果f'{a} {b}'的效率不及+ = b或a + b,我看不出它如何有效地响应一个专门询问性能的问题。此功能是语法糖(可以肯定的是有用的糖),而不是性能优化。
–查尔斯·达菲(Charles Duffy)
11月18日23:10
#4 楼
就稳定性和交叉实现而言,最糟糕的串联方法是使用'+'进行就地字符串串联,因为它不支持所有值。 PEP8标准不鼓励这样做,并鼓励长期使用format(),join()和append()。如链接的“编程建议”部分所引用:
例如,对于C语言中的语句,请不要依赖于CPython有效的就地字符串连接实现。形式a + = b或a = a + b。即使在CPython中,这种优化也是脆弱的(仅适用于某些类型),并且在不使用引用计数的实现中根本不存在这种优化。在库的性能敏感部分中,应改用''.join()形式。这将确保在各种实现中串联发生线性连接。
评论
参考链接本来很好:)
–user4911648
18年2月1日在9:19
#5 楼
推荐的方法仍然是使用附加和联接。评论
从我的回答中可以看出,这取决于要连接的字符串数。我已经为此做了一些时间安排(请参阅我在对我的答案的评论中链接到的谈话),并且通常除非超过十个,否则请使用+。
– Lennart Regebro
13年8月23日在6:39
PEP8提到了这一点(python.org/dev/peps/pep-0008/#programming-recommendations)。合理的是,尽管CPython对使用+ =的字符串连接进行了特殊的优化,但其他实现可能没有。
–Quantum7
17年7月7日在12:04
#6 楼
如果要连接的字符串是文字,请使用字符串文字连接re.compile(
"[A-Za-z_]" # letter or underscore
"[A-Za-z0-9_]*" # letter, digit or underscore
)
如果要注释字符串的一部分(如上所述),或者想要对部分文字而不是全部文字使用原始字符串或三引号。
#7 楼
您可以编写此函数def str_join(*args):
return ''.join(map(str, args))
然后您可以在任何需要的地方简单调用
str_join('Pine') # Returns : Pine
str_join('Pine', 'apple') # Returns : Pineapple
str_join('Pine', 'apple', 3) # Returns : Pineapple3
评论
str_join = lambda * str_list:''.join(s为str_list中的s)
– Rick支持Monica
17年7月27日在2:05
#8 楼
虽然有些过时,但在本节中,像Pythonista一样的代码:Idiomatic Python建议使用join()
而不是+
。如同PythonSpeedPerformanceTips在其有关字符串连接的部分中一样,具有以下免责声明:本节的准确性与Python的更高版本有关。在CPython 2.5中,字符串连接相当快,尽管这可能不适用于其他Python实现。有关讨论,请参见ConcatenationTestCode。
#9 楼
如@jdi所述,Python文档建议使用str.join
或io.StringIO
进行字符串连接。并表示,即使自Python 2.4开始进行了优化,开发人员也应期待+=
的二次时间。答案如下:如果Python检测到left参数没有其他引用,它将调用
realloc
来尝试通过调整字符串的大小来避免复制。这不是您应该依靠的东西,因为它是一个实现细节,并且因为如果realloc
最终需要频繁移动字符串,则性能无论如何都会降低到O(n ^ 2)。我将展示一个真实代码的示例,该示例天真地依赖于这种优化,但是并没有应用。下面的代码将可迭代的短字符串转换成较大的块,以供批量API使用。以下是具有建议的数据结构的替代方案:
def test_concat_chunk(seq, split_by):
result = ['']
for item in seq:
if len(result[-1]) + len(item) > split_by:
result.append('')
result[-1] += item
return result
和微基准:
import io
def test_stringio_chunk(seq, split_by):
def chunk():
buf = io.StringIO()
size = 0
for item in seq:
if size + len(item) <= split_by:
size += buf.write(item)
else:
yield buf.getvalue()
buf = io.StringIO()
size = buf.write(item)
if size:
yield buf.getvalue()
return list(chunk())
def test_join_chunk(seq, split_by):
def chunk():
buf = []
size = 0
for item in seq:
if size + len(item) <= split_by:
buf.append(item)
size += len(item)
else:
yield ''.join(buf)
buf.clear()
buf.append(item)
size = len(item)
if size:
yield ''.join(buf)
return list(chunk())
#10 楼
您可以用不同的方式进行操作。 str1 = "Hello"
str2 = "World"
str_list = ['Hello', 'World']
str_dict = {'str1': 'Hello', 'str2': 'World'}
# Concatenating With the + Operator
print(str1 + ' ' + str2) # Hello World
# String Formatting with the % Operator
print("%s %s" % (str1, str2)) # Hello World
# String Formatting with the { } Operators with str.format()
print("{}{}".format(str1, str2)) # Hello World
print("{0}{1}".format(str1, str2)) # Hello World
print("{str1} {str2}".format(str1=str_dict['str1'], str2=str_dict['str2'])) # Hello World
print("{str1} {str2}".format(**str_dict)) # Hello World
# Going From a List to a String in Python With .join()
print(' '.join(str_list)) # Hello World
# Python f'strings --> 3.6 onwards
print(f"{str1} {str2}") # Hello World
我通过以下文章创建了这个小摘要。
Python 3的f字符串:改进的字符串格式语法(指南)(还包括速度测试)
格式化的字符串文字
字符串串联和在Python中格式化
分割,串联和连接字符串
#11 楼
您也可以使用此(更有效)。 (https://softwareengineering.stackexchange.com/questions/304445/why-is-s-better-than-for-concatenation)s += "%s" %(stringfromelsewhere)
#12 楼
我的用例略有不同。我必须构造一个查询,其中有20多个字段是动态的。我遵循了这种使用格式方法的方法
使用+或其他方式的说明
评论
stackoverflow.com/questions/10043636/…