将两个字符串拼接起来,最符合 Python 风格的方法是什么?
例如:
输入:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
输出:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
将两个字符串拼接起来,最符合 Python 风格的方法是什么?
例如:
输入:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
输出:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
对于我来说,最具有Python特色的方法是以下方式,基本上做了相同的事情,但使用+
运算符来连接每个字符串中的单个字符:
res = "".join(i + j for i, j in zip(u, l))
print(res)
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
这也比使用两个 join()
调用更快:
In [5]: l1 = 'A' * 1000000; l2 = 'a' * 1000000
In [6]: %timeit "".join("".join(item) for item in zip(l1, l2))
1 loops, best of 3: 442 ms per loop
In [7]: %timeit "".join(i + j for i, j in zip(l1, l2))
1 loops, best of 3: 360 ms per loop
zip
在短字符串结束时停止迭代。在这种情况下,应该使用 zip_longest
(在Python 2中为 izip_longest
)从 itertools
模块确保两个字符串都被完全处理。
*引用自《Python之禅》:可读性很重要。
对我来说,Pythonic = 可读性;i + j
至少在我看来更容易被视觉解析。
"".join([i + j for i, j in zip(l1, l2)])
,这肯定是最快的方式。 - Padraic Cunningham"".join(map("".join, zip(l1, l2)))
更快,虽然不一定更符合Python的风格。 - Aleksi Torhamo另一种方法:
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
print(''.join(res))
输出:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
看起来速度更快了:
%%timeit
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
''.join(res)
100000 loops, best of 3: 4.75 µs per loop
比迄今为止最快的解决方案更快:
%timeit "".join(list(chain.from_iterable(zip(u, l))))
100000 loops, best of 3: 6.52 µs per loop
对于更长的字符串:
l1 = 'A' * 1000000; l2 = 'a' * 1000000
%timeit "".join(list(chain.from_iterable(zip(l1, l2))))
1 loops, best of 3: 151 ms per loop
%%timeit
res = [''] * len(l1) * 2
res[::2] = l1
res[1::2] = l2
''.join(res)
10 loops, best of 3: 92 ms per loop
Python 3.5.1。
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijkl'
zip()
函数)min_len = min(len(u), len(l))
res = [''] * min_len * 2
res[::2] = u[:min_len]
res[1::2] = l[:min_len]
print(''.join(res))
输出:
AaBbCcDdEeFfGgHhIiJjKkLl
itertools.zip_longest(fillvalue='')
)min_len = min(len(u), len(l))
res = [''] * min_len * 2
res[::2] = u[:min_len]
res[1::2] = l[:min_len]
res += u[min_len:] + l[min_len:]
print(''.join(res))
输出:
AaBbCcDdEeFfGgHhIiJjKkLlMNOPQRSTUVWXYZ
[''] * len(u)
,然后丢弃它。最好改为 [''] * (len(u) * 2)
。 - Kelly Bundy使用join()
和zip()
函数。
>>> ''.join(''.join(item) for item in zip(u,l))
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
u
和l
两个列表中相应位置的元素进行交替排列,并将结果合并成一个字符串,可以使用以下代码实现:''.join(itertools.chain.from_iterable(zip(u, l)))
。 - Blenderzip
会在较短的列表被完全迭代后停止。 - SuperBiasedManitertools.zip_longest
。 - TigerhawkT3在Python 2中,远远比使用列表切片快的方式是使用 字符串切片。对于小字符串而言速度约为列表切片的3倍,而对于长字符串则约为30倍。
res = bytearray(len(u) * 2)
res[::2] = u
res[1::2] = l
str(res)
然而,这种方法在Python 3上无法使用。不过,你可以实现类似于
res = bytearray(len(u) * 2)
res[::2] = u.encode("ascii")
res[1::2] = l.encode("ascii")
res.decode("ascii")
但是如果你处理的是短字符串,通过切片来完成这个任务已经不如直接使用循环了(对于长字符串而言,直接使用循环速度慢20倍)。并且这种方法还无法处理非ASCII字符。
顺便说一句,如果你需要处理海量字符串,并且每一个周期都至关重要,并且不得不使用Python字符串...以下是实现方法:
res = bytearray(len(u) * 4 * 2)
u_utf32 = u.encode("utf_32_be")
res[0::8] = u_utf32[0::4]
res[1::8] = u_utf32[1::4]
res[2::8] = u_utf32[2::4]
res[3::8] = u_utf32[3::4]
l_utf32 = l.encode("utf_32_be")
res[4::8] = l_utf32[0::4]
res[5::8] = l_utf32[1::4]
res[6::8] = l_utf32[2::4]
res[7::8] = l_utf32[3::4]
res.decode("utf_32_be")
特别处理较小类型的常见情况也会有所帮助。顺便说一下,对于长字符串而言,这只是列表切片速度的3倍,而对于小字符串而言则慢4到5倍。
无论如何,我更喜欢使用join
解决方案,但既然时间已经在其他地方提到了,那么我也可以加入进来。
如果你想要最快的方法,你可以将itertools与operator.add
结合使用:
In [36]: from operator import add
In [37]: from itertools import starmap, izip
In [38]: timeit "".join([i + j for i, j in uzip(l1, l2)])
1 loops, best of 3: 142 ms per loop
In [39]: timeit "".join(starmap(add, izip(l1,l2)))
1 loops, best of 3: 117 ms per loop
In [40]: timeit "".join(["".join(item) for item in zip(l1, l2)])
1 loops, best of 3: 196 ms per loop
In [41]: "".join(starmap(add, izip(l1,l2))) == "".join([i + j for i, j in izip(l1, l2)]) == "".join(["".join(item) for item in izip(l1, l2)])
Out[42]: True
但是将izip
和chain.from_iterable
组合使用,速度更快。
In [2]: from itertools import chain, izip
In [3]: timeit "".join(chain.from_iterable(izip(l1, l2)))
10 loops, best of 3: 98.7 ms per loop
chain(*
和chain.from_iterable(...
之间也存在显著差异。
In [5]: timeit "".join(chain(*izip(l1, l2)))
1 loops, best of 3: 212 ms per loop
使用join的生成器是不存在的,将其作为参数传递总是会更慢,因为Python会首先使用一次扫描数据来确定所需的大小,然后再进行实际的连接操作。这在使用生成器时是不可能实现的:
/* Here is the general case. Do a pre-pass to figure out the total
* amount of space we'll need (sz), and see whether all arguments are
* bytes-like.
*/
如果您有不同长度的字符串,并且不想丢失数据,可以使用izip_longest :
In [22]: from itertools import izip_longest
In [23]: a,b = "hlo","elworld"
In [24]: "".join(chain.from_iterable(izip_longest(a, b,fillvalue="")))
Out[24]: 'helloworld'
对于Python 3,它被称为zip_longest
但是对于Python2,veedrac的建议是目前最快的:
In [18]: %%timeit
res = bytearray(len(u) * 2)
res[::2] = u
res[1::2] = l
str(res)
....:
100 loops, best of 3: 2.68 ms per loop
list
? - Copperfield"".join(list(...))
给我 6.715280318699769,而 timeit "".join(starmap(...))
给我 6.46332361384313。 - Copperfield"".join(list(starmap(add, izip(l1,l2))))
比"".join(starmap(add, izip(l1,l2)))
慢。我在我的机器上使用Python 2.7.11和Python 3.5.1运行测试,甚至在www.python.org的虚拟控制台中使用Python 3.4.3,所有的结果都是一样的,我运行了几次,结果总是一样的。 - Copperfieldlist(...)
速度较慢的问题,手动调用list
并不能提高速度。之所以推荐使用"".join([x for x in y])
而不是"".join(x for x in y)
,是因为后者创建了一个生成器,具有暂停-恢复开销。使用"".join(list(x for x in y))
也无法改善速度问题。 - Veedrac您还可以使用map
和operator.add
来实现此操作:
from operator import add
u = 'AAAAA'
l = 'aaaaa'
s = "".join(map(add, u, l))
输出:
'AaAaAaAaAa'
map函数会从第一个可迭代对象u
中取出每个元素,从第二个可迭代对象l
中取出相应元素,并且将这两个元素作为参数传入第一个参数add
所指定的函数中进行计算,最后使用join方法将它们连接起来。
Jim的回答很棒,但这是我最喜欢的选项,如果你不介意导入一些内容:
from functools import reduce
from operator import add
reduce(add, map(add, u, l))
u = "foobar"
l = "baz"
mesh(u,l) = "fboaozbar"
首先,您需要执行以下操作:
def mesh(a,b):
minlen = min(len(a),len(b))
return "".join(["".join(x+y for x,y in zip(a,b)),a[minlen:],b[minlen:]])
我喜欢使用两个for
循环,变量名可以提示/提醒正在发生的事情:
"".join(char for pair in zip(u,l) for char in pair)
考虑到使用双重列表推导来处理n个字符串的时间复杂度为O(1), 不这样做感觉有点不符合Pythonic的风格:
"".join(c for cs in itertools.zip_longest(*all_strings) for c in cs)
其中all_strings
是您想要交错的字符串列表。在您的情况下,all_strings = [u, l]
。一个完整的使用示例如下:
import itertools
a = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
b = 'abcdefghijklmnopqrstuvwxyz'
all_strings = [a,b]
interleaved = "".join(c for cs in itertools.zip_longest(*all_strings) for c in cs)
print(interleaved)
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
像很多答案一样,它可能不是最快的,但是简单且灵活。而且,不需要太多的复杂性,这比被接受的答案略微快一些(通常情况下,字符串相加在Python中有点慢):
In [7]: l1 = 'A' * 1000000; l2 = 'a' * 1000000;
In [8]: %timeit "".join(a + b for i, j in zip(l1, l2))
1 loops, best of 3: 227 ms per loop
In [9]: %timeit "".join(c for cs in zip(*(l1, l2)) for c in cs)
1 loops, best of 3: 198 ms per loop