大多数pythonic方式交错两个字符串

Bra*_*Deo 115 python string python-2.7 python-3.x

将两个字符串拼接在一起的最pythonic方法是什么?

例如:

输入:

u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Run Code Online (Sandbox Code Playgroud)

输出:

'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Run Code Online (Sandbox Code Playgroud)

Jim*_*ard 127

对我来说,最pythonic*方式是以下几乎相同的东西,但使用+运算符连接每个字符串中的单个字符:

res = "".join(i + j for i, j in zip(u, l))
print(res)
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Run Code Online (Sandbox Code Playgroud)

它也比使用两个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
Run Code Online (Sandbox Code Playgroud)

存在更快的方法,但它们经常使代码混淆.

注意:如果两个输入字符串是相同的长度,则较长的一个将被截断,zip停在较短字符串的结尾迭代.在这种情况下,zip不应该从模块中使用zip_longest(izip_longest在Python 2中)itertools以确保两个字符串都完全耗尽.


*引用Python的Zen:可读性计数.
Pythonic = 对我来说可读性 ; i + j只是在视觉上解析,至少对我的眼睛而言.

  • `"".join(map("".join,zip(l1,l2)))`甚至更快,虽然不一定更pythonic. (6认同)
  • 运行`"".join([i + j for i,j in zip(l1,l2)])`它肯定会是最快的 (5认同)

Mik*_*ler 62

更快的替代方案

其他方式:

res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
print(''.join(res))
Run Code Online (Sandbox Code Playgroud)

输出:

'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Run Code Online (Sandbox Code Playgroud)

速度

看起来更快:

%%timeit
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
''.join(res)

100000 loops, best of 3: 4.75 µs per loop
Run Code Online (Sandbox Code Playgroud)

比目前为止最快的解决方案:

%timeit "".join(list(chain.from_iterable(zip(u, l))))

100000 loops, best of 3: 6.52 µs per loop
Run Code Online (Sandbox Code Playgroud)

也适用于较大的字符串:

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
Run Code Online (Sandbox Code Playgroud)

Python 3.5.1.

具有不同长度的字符串的变体

u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijkl'
Run Code Online (Sandbox Code Playgroud)

较短的一个确定长度(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))
Run Code Online (Sandbox Code Playgroud)

输出:

AaBbCcDdEeFfGgHhIiJjKkLl
Run Code Online (Sandbox Code Playgroud)

更长的一个确定长度(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))
Run Code Online (Sandbox Code Playgroud)

输出:

AaBbCcDdEeFfGgHhIiJjKkLlMNOPQRSTUVWXYZ
Run Code Online (Sandbox Code Playgroud)


Tig*_*kT3 49

随着join()zip().

>>> ''.join(''.join(item) for item in zip(u,l))
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Run Code Online (Sandbox Code Playgroud)

  • 或者'''.join(itertools.chain.from_iterable(zip(u,l)))` (17认同)
  • @SuperBiasedMan - 是的.如果它成为一个问题,可以使用`itertools.zip_longest`. (5认同)

Vee*_*rac 18

在Python 2上,到目前为止,更快的做事方式是,对于小字符串列表切片的速度约为3倍,对于长字符串切换速度约为30倍,

res = bytearray(len(u) * 2)
res[::2] = u
res[1::2] = l
str(res)
Run Code Online (Sandbox Code Playgroud)

但是,这不适用于Python 3.你可以实现类似的东西

res = bytearray(len(u) * 2)
res[::2] = u.encode("ascii")
res[1::2] = l.encode("ascii")
res.decode("ascii")
Run Code Online (Sandbox Code Playgroud)

但到那时你已经失去了对小字符串列表切片的收益(它仍然是长字符串速度的20倍),这甚至不适用于非ASCII字符.

FWIW,如果你在大量的串这样做的,需要每个周期,以及由于某种原因必须使用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")
Run Code Online (Sandbox Code Playgroud)

特殊套管较小型的常见情况也会有所帮助.FWIW,这只是长字符串列表切片速度的3倍,而小字符串则 4到5倍.

无论哪种方式,我更喜欢这些join解决方案,但由于其他地方提到时间,我想我也可以加入.


Pad*_*ham 16

如果你想要最快的方式,你可以将itertoolsoperator.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
Run Code Online (Sandbox Code Playgroud)

但结合izipchain.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
Run Code Online (Sandbox Code Playgroud)

chain(*和之间也存在很大差异 chain.from_iterable(....

In [5]: timeit "".join(chain(*izip(l1, l2)))
1 loops, best of 3: 212 ms per loop
Run Code Online (Sandbox Code Playgroud)

没有像连接器这样的东西,传递一个总是会变慢,因为python将首先使用内容构建一个列表,因为它对数据进行两次传递,一次计算所需的大小,一个实际执行使用生成器无法实现的连接:

join.h:

 /* 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.
   */
Run Code Online (Sandbox Code Playgroud)

此外,如果您有不同的长度字符串并且您不想丢失数据,则可以使用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'
Run Code Online (Sandbox Code Playgroud)

对于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
Run Code Online (Sandbox Code Playgroud)

  • 为什么`列表'?不需要 (2认同)

roo*_*oot 12

您也可以使用map和执行此操作operator.add:

from operator import add

u = 'AAAAA'
l = 'aaaaa'

s = "".join(map(add, u, l))
Run Code Online (Sandbox Code Playgroud)

输出:

'AaAaAaAaAa'
Run Code Online (Sandbox Code Playgroud)

映射的作用是从第一个iterable中获取每个元素,u从第二个iterable中获取第一个元素,l并应用作为第一个参数提供的函数add.然后加入就加入了他们.


kni*_*ite 9

吉姆的答案很棒,但如果您不介意几种进口,这里是我最喜欢的选择:

from functools import reduce
from operator import add

reduce(add, map(add, u, l))
Run Code Online (Sandbox Code Playgroud)

  • 他说大多数Pythonic,而不是大多数Haskellic;) (7认同)

Chr*_*son 7

很多这些建议都假定字符串长度相等.也许这涵盖了所有合理的用例,但至少在我看来,你似乎也想要容纳不同长度的字符串.或者我是唯一一个认为网格应该像这样工作的人:

u = "foobar"
l = "baz"
mesh(u,l) = "fboaozbar"
Run Code Online (Sandbox Code Playgroud)

一种方法是:

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:]])
Run Code Online (Sandbox Code Playgroud)


Nea*_*ltz 5

我喜欢使用两个fors,变量名称可以提示/提醒正在发生的事情:

"".join(char for pair in zip(u,l) for char in pair)
Run Code Online (Sandbox Code Playgroud)