大多数pythonic方式交错两个string
将两个string网格化在一起最麻烦的方法是什么?
例如:
input:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' l = 'abcdefghijklmnopqrstuvwxyz'
输出:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
对我来说,最pythonic *方法是几乎相同的事情,但使用+
运算符连接每个string中的单个字符:
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
存在更快的方法,但它们经常混淆代码。
注意:如果两个inputstring的长度不一样,那么较长的string将被截断,因为zip
停止在较短string的末尾迭代。 在这种情况下,应该使用itertools
模块中的zip_longest
(Python 2中的izip_longest
)来代替zip
以确保两个string都完全耗尽。
*从Python的禅宗引用: 可读性计数 。
Pythonic = 可读性 ; i + j
只是在视觉上更容易parsing,至less在我眼中。
更快的select
其他方式:
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
比迄今为止最快的解决scheme:
%timeit "".join(list(chain.from_iterable(zip(u, l)))) 100000 loops, best of 3: 6.52 µs per loop
对于更大的string:
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。
不同长度的string的变化
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
join()
和zip()
。
>>> ''.join(''.join(item) for item in zip(u,l)) 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
在Python 2中, 到目前为止 ,做事情的速度更快,大约3倍的小string的列表切片的速度和长的约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")
但是到那时你已经在小string的列表切片上失去了收益(它仍然是长string速度的20倍),这对于非ASCII字符还是不起作用的。
FWIW,如果你是在大量的string上进行这样的操作,并且需要每个周期, 并且出于某种原因必须使用Pythonstring…下面是如何做到这一点:
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")
小型的常见情况也会有所帮助。 FWIW,对于小string来说,这只是列表切片速度的3倍,对于小string来说,速度却只有4到5倍。
无论哪种方式,我更喜欢join
解决scheme,但由于时间在其他地方提到,所以我想我也可以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将首先build立一个列表使用的内容,因为它做了两遍数据,一个找出所需的大小,一个实际上使用生成器不可能的连接:
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. */
另外如果你有不同的长度string,你不想丢失数据,你可以使用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的build议是迄今为止最快的:
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
你也可以使用map
和operator.add
来做到这一点:
from operator import add u = 'AAAAA' l = 'aaaaa' s = "".join(map(add, u, l))
输出 :
'AaAaAaAaAa'
这个映射是从第一个可迭代的u
和第二个可迭代的l
的第一个元素开始的,并应用作为第一个参数add
提供的函数。 然后join只是join他们。
很多这些build议都假定string长度相等。 也许这涵盖了所有合理的用例,但至less在我看来,你似乎也想要容纳不同长度的string。 或者我是唯一一个认为网格应该有点像这样工作的人:
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:]])
吉姆的回答非常好,但是这里是我最喜欢的select,如果你不介意几个import:
from functools import reduce from operator import add reduce(add, map(add, u, l))
我喜欢使用两个s,variables名称可以提示/提醒发生了什么事情:
"".join(char for pair in zip(u,l) for char in pair)
只是增加另一个更基本的方法:
st = "" for char in u: st = "{0}{1}{2}".format( st, char, l[ u.index( char ) ] )
比当前领先的解决scheme可能更快,更短:
from itertools import chain u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' l = 'abcdefghijklmnopqrstuvwxyz' res = "".join(chain(*zip(u, l)))
速度策略是尽可能地在C级做尽可能多的事情。 相同的zip_longest()修复了不均匀的string,它会和chain()一样从同一个模块中出来,所以不能在这里给我太多点!
我一路上想到的其他解决scheme:
res = "".join(u[x] + l[x] for x in range(len(u))) res = "".join(k + l[i] for i, k in enumerate(u))
感觉有点不pythonic在这里不考虑双列表理解的答案,以O(1)努力处理nstring:
"".join(c for cs in itertools.zip_longest(*all_strings) for c in cs)
其中all_strings
是您要交错的string的列表。 在你的情况下, 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'
像许多答案一样,最快? 可能不是,但简单而灵活。 另外,没有太多额外的复杂性,这比接受的答案稍微快一些(一般来说,string添加在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
你可以使用iteration_utilities.roundrobin
1
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' l = 'abcdefghijklmnopqrstuvwxyz' from iteration_utilities import roundrobin ''.join(roundrobin(u, l)) # returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
或同一个包中的ManyIterables
类:
from iteration_utilities import ManyIterables ManyIterables(u, l).roundrobin().as_string() # returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
1这是来自我写的第三方库: iteration_utilities
。
我会使用zip()来获得一个可读和简单的方法:
result = '' for cha, chb in zip(u, l): result += '%s%s' % (cha, chb) print result # 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'