web-dev-qa-db-ja.com

2つの文字列をインターリーブする最もPython的な方法

2つの文字列を一緒にメッシュする最もPython的な方法は何ですか?

例えば:

入力:

u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'

出力:

'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
113
Brandon Deo

私にとって、ほとんどの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_は、少なくとも私の目には、視覚的に簡単に解析されるだけです。

126

より速い代替

別の方法:

_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
_
62
Mike Müller

join()およびZip()を使用します。

>>> ''.join(''.join(item) for item in Zip(u,l))
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
48
TigerhawkT3

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ソリューションを好むが、タイミングが他の場所で言及されたので、私も参加するかもしれないと思った。

18
Veedrac

最速の方法が必要な場合は、 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

ただし、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

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
16

map および operator.add

from operator import add

u = 'AAAAA'
l = 'aaaaa'

s = "".join(map(add, u, l))

出力

'AaAaAaAaAa'

マップは、最初の反復可能なuからすべての要素を取得し、2番目の反復可能なlから最初の要素を取得し、最初の引数addとして提供される関数を適用します。その後、参加するだけで参加します。

12
root

Jimの答えは素晴らしいですが、いくつかのインポートを気にしないのであれば、ここに私のお気に入りのオプションがあります。

from functools import reduce
from operator import add

reduce(add, map(add, u, l))
9
knite

これらの提案の多くは、文字列の長さが等しいと仮定しています。多分それはすべての合理的なユースケースをカバーしていますが、少なくとも私にとっては、異なる長さの文字列にも対応したいと思うかもしれません。または、メッシュが次のように少し動作するはずだと思っているのは私だけです:

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:]])
7

私は2つのforsを使用するのが好きです。変数名は何が起こっているのかについてのヒント/リマインダーを与えることができます:

"".join(char for pair in Zip(u,l) for char in pair)
5
Neal Fultz

別のより基本的なアプローチを追加するだけです:

st = ""
for char in u:
    st = "{0}{1}{2}".format( st, char, l[ u.index( char ) ] )
4
WeRelic

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
3
scnerd

iteration_utilities.roundrobin1

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

3
MSeifert

現在の主要なソリューションよりも潜在的に高速で短い:

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))
3
cdlane

Zip()を使用して、読みやすく簡単な方法を取得します。

result = ''
for cha, chb in Zip(u, l):
    result += '%s%s' % (cha, chb)

print result
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
2
valeas