Python中打印字符串的最有效方法是什么?

3
根据优化提示,在 http://wiki.python.org/moin/PythonSpeed/PerformanceTips,连接字符串应该使用: out = "<html>%(head)s%(prologue)s%(query)s%(tail)s</html>" % locals() 而不是: out = "<html>" + head + prologue + query + tail + "</html>" 我的问题是,如果我想打印而不是存储值,这个方法是否也适用?此外,将连续的打印语句放在一行上会更快吗?例如,使用以下代码是否更好:
```python print(head, end="") print(prologue, end="") print(query, end="") print(tail, end="") ```
print "Some word"
print "Another line"
print "something else"

或者
print '''Some word
Another line
something else'''

谢谢您的提前帮助!


1
默认情况下,“print”会在字符串末尾添加换行符,因此您的第一个示例与第二个示例不等价。 - Andrew
谢谢,安德鲁,我漏掉了这个,我会修复它。 - Parker
1
请参考 Joel Spolsky 的《回归基础》(Back to Basics)一文,讨论此问题。尽管这篇文章主要讨论 C 语言,但它所讨论的问题同样适用于 Python (以及许多其他语言)。 - Brian
如果你不需要支持 Python2.6 以前的版本,建议使用 str.format 进行插值。例如:print "我的名字是 {name}".format(name='Parker') - John La Rooy
我认为控制台输出所需的时间会使字符串构建方式的差异相形见绌。一旦开始打印,所有操作都会变慢。 - Russell Borogove
3个回答

6

字符串连接在仅存在一个对字符串的引用时得到了改进。请参阅stringobject.c中的PyString_ConcatAndDel。这种情况相当常见,通常循环中的连接是线性的,因为只有一个对字符串的引用。

下面是一个简单的实验来演示这种行为。当没有足够的空间扩展字符串时,id()会发生变化。

>>> s = ""
>>> prev_id = None
>>> for i in range(1000):
...  s += "*"
...  if prev_id != id(s):
...   print id(s), len(s)
...   prev_id = id(s)
... 
3077352864 1
3077437728 2
3077434328 9
3077428384 17
3077379928 25
3077291808 33
3077712448 41
3077358800 49
3077394728 57
3077667680 65
3077515120 73
3077354176 81
3077576488 89
3077559200 97
3077414248 105
3077670336 113
3077612160 121
3077707040 129
3077526040 137
3077571472 145
3077694944 153
3077595936 161
3077661904 169
3077552608 177
3077715680 185
3077583776 193
3077244304 201
3077604560 209
3077510392 217
3077334304 225
144468768 233
144787416 245
144890104 389

很棒的东西,我得坚持katrielalex的答案,但你因为为我解释这个问题而得到了+1!我想你以前在这里回答过我的问题,所以一如既往地感谢! - Parker
我觉得 Python 在这种优化中使用的速度递增率很有趣,而且我们两个最终得到了不同的输出结果。我习惯于使用相当无聊的算法,只是通过一个常数因子(通常为 2)来增加容量,但显然 Python 做了些不同的事情。无论哪种方式,这都使得附加操作成为一个线性操作。 - Brian

5
您的问题并不是关于打印字符串的最有效方法,而是关于格式化输出,无论如何都应该使用format,因为它比简单的连接更强大。但是,以下是一些有关连接的注意事项。

编辑:重写以包含一些细节

打印不相关。重要的是,由于某些语言处理字符串连接的方式,连接许多字符串可能具有二次阶数。 (非常天真和基础)推理是,要连接两个字符串,您必须遍历第一个字符串的所有字符,然后附加第二个字符串的所有字符。因此,如果您正在连接十个字符串,则首先遍历第一个字符串并附加第二个字符串,然后遍历第一个+第二个并附加第三个,然后遍历第一个+第二个+第三个并附加第四个,依此类推。

因此,天真的连接实现将使您做比所需更多的工作。确实,在Python的早期版本中,这是一个问题。但是,@gnibbler在评论中指出,后来的版本现在通常会对此进行优化,从而完全消除了这一点。

Python用于连接字符串的习惯用法是"".join(...)。这完全绕过了任何可能的问题,并且是标准的习惯用法。如果您想要通过附加构建字符串,请查看StringIO

>>> from io import StringIO
>>> foo = StringIO()
>>> for letter in map(chr, range(128)):
...     foo.write(letter)
...
>>> foo.seek(0)
0
>>> foo.read()
'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\
x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABC
DEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f'

2
此外,这个拼接问题不仅限于Python语言,而是涉及到多种编程语言。另外,这也是一个非常流行的面试问题。 - Brian
好的,谢谢!那么只是为了澄清一下,打印语句的数量并不重要,但是当我使用print时,不应该使用 '+' 吗? - Parker
1
请注意,在Python 2.5及更高版本中,“使用+连接多个字符串实际上具有二次阶数”的说法不再正确(但我仍然喜欢使用其他的表达方式来代替它)。 - bgporter
3
@jimbob,拼接并不总是二次的 - 请参见http://stackoverflow.com/questions/4128494 - John La Rooy
2
值得注意的是,对于少量字符串来说,连接操作的实际执行速度比任何其他替代方案都要快得多。此外,在这里,join 几乎完全无关紧要,因为问题是关于插值的,而 format 方法更快,join 是无用的。 - aaronasterling
显示剩余4条评论

2
打印时无需进行连接:
print "<html>", head, prologue, query, tail, "</html>"

这个与之前的例子相同(结尾处的逗号可以防止 \n):

print "<html>",
print head,
...
print "</html>"

我认为答案是否定的,不要仅仅为了打印而连接字符串,这会使事情变得更慢。但你真的不应该只听我的,写几个测试并使用timeit进行分析。


这在Python3中使用print函数也可以正常工作,您可以指定自己的分隔符。例如:print("foo", "bar", sep="-") - John La Rooy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接