2つの文字列を一緒にメッシュする最もPython的な方法は何ですか?
例えば:
入力:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
出力:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
私にとって、ほとんどのPython *の方法は次のとおりですはほぼ同じことを行いますが、各文字列の個々の文字を連結するために_+
_演算子を使用します:
_res = "".join(i + j for i, j in Zip(u, l))
print(res)
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
_
また、2つの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
_
より高速なアプローチが存在しますが、多くの場合、コードを難読化します。
注:2つの入力文字列がnot同じ長さの場合、長い方は切り捨てられますZip
は、短い文字列の最後で繰り返しを停止します。この場合、Zip
の代わりに_Zip_longest
_(_izip_longest
_in Python 2)from itertools
モジュールは、両方の文字列が完全に使い果たされるようにします。
*PythonのZenから引用を取得するには:読みやすさのカウント。
Pythonic =可読性; _i + j
_は、少なくとも私の目には、視覚的に簡単に解析されるだけです。
別の方法:
_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
_
join()
およびZip()
を使用します。
>>> ''.join(''.join(item) for item in Zip(u,l))
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
On Python 2、farにより高速な方法で、小さな文字列のリストスライスの最大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文字でも機能しません。
FWIW、areこれを大規模な文字列で行い、すべてのサイクルが必要な場合、andは何らかの理由で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")
小さい型の一般的なケースを特別にケーシングすることも役立ちます。 FWIW、これは長い文字列の場合、リストスライスの3倍の速度で、4〜5の係数です。小さな文字列の場合は、slowerです。
いずれにせよ、私は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
pythonは最初にコンテンツを使用してリストを作成します。これはデータに対して2回のパスを実行するためです。必要なサイズと、ジェネレータを使用しては不可能な結合を実際に行うためのサイズ:
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.
*/
また、異なる長さの文字列があり、データを失いたくない場合は、 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
map
および operator.add
:
from operator import add
u = 'AAAAA'
l = 'aaaaa'
s = "".join(map(add, u, l))
出力:
'AaAaAaAaAa'
マップは、最初の反復可能なu
からすべての要素を取得し、2番目の反復可能なl
から最初の要素を取得し、最初の引数add
として提供される関数を適用します。その後、参加するだけで参加します。
Jimの答えは素晴らしいですが、いくつかのインポートを気にしないのであれば、ここに私のお気に入りのオプションがあります。
from functools import reduce
from operator import add
reduce(add, map(add, u, l))
これらの提案の多くは、文字列の長さが等しいと仮定しています。多分それはすべての合理的なユースケースをカバーしていますが、少なくとも私にとっては、異なる長さの文字列にも対応したいと思うかもしれません。または、メッシュが次のように少し動作するはずだと思っているのは私だけです:
u = "foobar"
l = "baz"
mesh(u,l) = "fboaozbar"
これを行う1つの方法は次のとおりです。
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:]])
私は2つのfor
sを使用するのが好きです。変数名は何が起こっているのかについてのヒント/リマインダーを与えることができます:
"".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 ) ] )
O(1)努力でn個の文字列を処理するために、ここでdouble-list-comprehensionの答えを考慮しないことをPythonに少し感じます:
"".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
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
。
現在の主要なソリューションよりも潜在的に高速で短い:
from itertools import chain
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
res = "".join(chain(*Zip(u, l)))
戦略的には、Cレベルでできるだけ多くのことを行うことが重要です。同じZip_longest()が不均一な文字列を修正し、chain()と同じモジュールから出てくるので、あまりにも多くのポイントをDingできません!
途中で思いついた他のソリューション:
res = "".join(u[x] + l[x] for x in range(len(u)))
res = "".join(k + l[i] for i, k in enumerate(u))
Zip()を使用して、読みやすく簡単な方法を取得します。
result = ''
for cha, chb in Zip(u, l):
result += '%s%s' % (cha, chb)
print result
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'