2つの文字列をメッシュ化する最もパイソン的な方法は何ですか?
例えば:
入力:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
出力:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
2つの文字列をメッシュ化する最もパイソン的な方法は何ですか?
例えば:
入力:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
出力:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
回答:
私にとって、最もpythonic *の方法は次のとおりです。これはほとんど同じことを行いますが、+
演算子を使用して各文字列の個々の文字を連結します。
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つの入力文字列が同じ長さではない場合、長い文字列は切り捨てられzip
、短い文字列の終わりで反復が停止します。この場合、代わりのzip
一つは使用すべきであるzip_longest
(izip_longest
からのPython 2)itertools
両方の文字列が完全に排出されることを確実にするためにモジュール。
* Zen of Pythonからの引用:読みやすさが重要です。
Pythonic = 私にとって読みやすさ。i + j
少なくとも私の目には、視覚的に解析しやすいだけです。
"".join([i + j for i, j in zip(l1, l2)])
、それが間違いなく最速になります
"".join(map("".join, zip(l1, l2)))
はより高速ではありますが、必ずしもよりパイソンではありません。
別の方法:
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'
''.join(itertools.chain.from_iterable(zip(u, l)))
zip
短いリストが完全に繰り返されたときに停止するため、一方が他方より短い場合、これはリストを切り捨てます。
itertools.zip_longest
問題が発生した場合に使用できます。
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文字ではまだ機能しません。
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")
小さい型の一般的なケースを特別なケースにすることも役立ちます。FWIW、これは長い文字列のリストスライスの速度の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
ジョイン付きのジェネレータなどというものはありません。Pythonは最初にコンテンツを使用してリストを作成するため、1つを渡すと常に遅くなります。1つは必要なサイズを把握するために、もう1つは実際に行うために、データを2回渡すためです。ジェネレーターを使用して不可能である結合:
/* 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
?不要です
"".join(list(...))
Give Me 6.715280318699769およびTimeit the "".join(starmap(...))
Give Me 6.46332361384313
"".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で実行し、すべて同じことを言って、何度か実行します。同じ
map
and を使用してこれを行うこともできますoperator.add
:
from operator import add
u = 'AAAAA'
l = 'aaaaa'
s = "".join(map(add, u, l))
出力:
'AaAaAaAaAa'
マップが行うことは、最初の反復可能オブジェクトのすべての要素u
と2番目の反復可能要素の最初の要素を取得l
し、最初の引数として指定された関数を適用することadd
です。その後、参加するだけで参加します。
これらの提案の多くは、文字列が同じ長さであることを前提としています。多分それはすべての合理的なユースケースをカバーしますが、少なくとも私にとっては、長さが異なる文字列にも対応したいと思うかもしれません。または、メッシュが次のように機能するのは私だけだと思いますか?
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:]])
O(1)の努力でn個の文字列を処理するために、ここでは二重リスト理解の答えを考慮しないために、少しun-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
現在の主要なソリューションよりも高速で短い可能性があります。
from itertools import chain
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
res = "".join(chain(*zip(u, l)))
戦略的な速度は、Cレベルで可能な限り多くを行うことです。同じzip_longest()が不均一な文字列を修正し、それはchain()と同じモジュールから出てくるので、そこにあまり多くのポイントを指定することはできません!
途中で私が思いついた他の解決策:
res = "".join(u[x] + l[x] for x in range(len(u)))
res = "".join(k + l[i] for i, k in enumerate(u))
あなたが使用することができます1iteration_utilities.roundrobin
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
。