두 개의 문자열을 함께 메시하는 가장 비단뱀적인 방법은 무엇입니까?
예를 들면 :
입력:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
산출:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
두 개의 문자열을 함께 메시하는 가장 비단뱀적인 방법은 무엇입니까?
예를 들면 :
입력:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
산출:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
답변:
나에게 가장 비단뱀적인 방법은 거의 같은 일을 하지만 +
각 문자열의 개별 문자를 연결하기 위해 연산자를 사용하는 다음과 같습니다 .
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
더 빠른 접근 방식이 있지만 종종 코드를 난독 화합니다.
참고 : 두 입력 문자열의 길이 가 같지 않으면 더 zip
짧은 문자열의 끝에서 반복을 중지 하므로 긴 문자열이 잘립니다 . 이 경우 대신 zip
하나를 사용해야합니다 zip_longest
( izip_longest
으로부터 파이썬 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
파이썬 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 배, 긴 문자열의 경우 ~ 30x입니다.
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, 당신이 경우에 하는 대규모 문자열에이 일을 모든 사이클을 필요로 하고 어떤 이유로 파이썬 문자열을 사용해야합니다 ... 여기 그 방법은 다음과 같습니다
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
조인이있는 생성기와 같은 것은 없습니다. 하나를 전달하는 것은 데이터에 대해 두 번의 전달을 수행하기 때문에 먼저 콘텐츠를 사용하여 목록을 작성하기 때문에 하나를 전달하는 것이 항상 느려질 것입니다. 하나는 필요한 크기를 파악하고 하나는 실제로 수행하기 때문입니다. 생성기로는 불가능한 조인 :
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'
파이썬 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(...))
나에게 6.715280318699769을주고 timeit은 "".join(starmap(...))
나에게 6.46332361384313 제공
"".join(list(starmap(add, izip(l1,l2))))
가 "".join(starmap(add, izip(l1,l2)))
. 나는 내 컴퓨터에서 파이썬 2.7.11과 파이썬 3.5.1에서 테스트를 실행한다. 심지어 www.python.org 의 가상 콘솔에서도 파이썬 3.4.3을 사용하고 모두 똑같이 말하고 몇 번 실행하고 항상 같은
map
및 operator.add
다음을 사용하여이 작업을 수행 할 수도 있습니다 .
from operator import add
u = 'AAAAA'
l = 'aaaaa'
s = "".join(map(add, u, l))
출력 :
'AaAaAaAaAa'
어떤 맵 않는 것은 처음부터 반복 가능한 모든 요소 걸립니다 u
번째 반복 가능한로부터 첫번째 요소를 l
첫 번째 인수로서 기능하고 적용 add
. 그런 다음 조인은 그들을 조인합니다.
이러한 제안의 대부분은 문자열 길이가 같다고 가정합니다. 아마도 모든 합리적인 사용 사례를 다룰 수 있지만 적어도 나에게는 길이가 다른 문자열도 수용하고 싶을 것 같습니다. 아니면 메시가 다음과 같이 작동해야한다고 생각하는 유일한 사람입니까?
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:]])
O (1) 노력으로 n 문자열을 처리하기 위해 여기에서 이중 목록 이해 답변을 고려하지 않는 것이 비단 식적이라고 느낍니다.
"".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'
많은 답변처럼 빠른가요? 아마 아니지만 간단하고 유연합니다. 또한 복잡성을 너무 많이 추가하지 않으면 허용되는 답변보다 약간 빠릅니다 (일반적으로 문자열 추가는 파이썬에서 약간 느립니다).
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))
1을 사용할 수 있습니다.iteration_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
..