อะไรคือวิธี pythonic ที่สุดในการเชื่อมโยงสองสายเข้าด้วยกัน?
ตัวอย่างเช่น:
การป้อนข้อมูล:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
เอาท์พุท:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
อะไรคือวิธี pythonic ที่สุดในการเชื่อมโยงสองสายเข้าด้วยกัน?
ตัวอย่างเช่น:
การป้อนข้อมูล:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
เอาท์พุท:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
คำตอบ:
สำหรับฉันวิธี pythonic * ส่วนใหญ่คือวิธีต่อไปนี้ซึ่งทำในสิ่งเดียวกันแต่ใช้ตัว+
ดำเนินการเพื่อต่ออักขระแต่ละตัวในแต่ละสตริง:
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
ใน Python 2) จากitertools
โมดูลแทนเพื่อให้แน่ใจว่าสตริงทั้งสองหมดอย่างสมบูรณ์
* ในการใช้คำพูดจากเซนของงูใหญ่ : นับการอ่าน
Pythonic = อ่านง่ายสำหรับฉัน; i + j
เพียงแค่แยกวิเคราะห์ทางสายตาได้ง่ายขึ้นอย่างน้อยก็สำหรับดวงตาของฉัน
"".join([i + j for i, j in zip(l1, l2)])
และมันจะเร็วที่สุดแน่นอน
"".join(map("".join, zip(l1, l2)))
เร็วขึ้นแม้ว่าจะไม่จำเป็นต้องเป็น pythonic มากกว่าก็ตาม
อีกวิธีหนึ่ง:
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
สามารถใช้ได้หากเกิดปัญหาขึ้น
เกี่ยวกับงูหลาม 2 โดยไกลวิธีที่เร็วกว่าที่จะทำสิ่งที่ ~ 3x ความเร็วของการหั่นรายการสำหรับสตริงขนาดเล็กและ ~ 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 หากคุณกำลังทำสิ่งนี้กับสตริงขนาดใหญ่และต้องการทุก ๆ รอบและด้วยเหตุผลบางอย่างต้องใช้สตริง 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 จะสร้างรายการโดยใช้เนื้อหาเป็นครั้งแรกเนื่องจากจะส่งผ่านข้อมูลสองรายการหนึ่งเพื่อหาขนาดที่ต้องการและอีกอันหนึ่งต้องทำจริง การเข้าร่วมซึ่งไม่สามารถทำได้โดยใช้เครื่องกำเนิดไฟฟ้า:
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
list
?? ไม่จำเป็น
"".join(list(...))
give me 6.715280318699769 and 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
และoperator.add
:
from operator import add
u = 'AAAAA'
l = 'aaaaa'
s = "".join(map(add, u, l))
เอาท์พุต :
'AaAaAaAaAa'
อะไรจะเป็นแผนที่จะใช้เวลาทุกองค์ประกอบจาก iterable แรกu
องค์ประกอบและเป็นครั้งแรกจาก iterable ที่สองและใช้ฟังก์ชั่นที่ให้มาเป็นอาร์กิวเมนต์แรก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"
วิธีหนึ่งในการดำเนินการดังต่อไปนี้:
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:]])
ฉันชอบใช้สอง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 ) ] )
รู้สึกอึดอัดเล็กน้อยที่จะไม่พิจารณาคำตอบแบบ double-list-comp understandion ที่นี่เพื่อจัดการ n string ด้วยความพยายาม O (1):
"".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
.
ฉันจะใช้ zip () เพื่อให้อ่านง่ายและสะดวก:
result = ''
for cha, chb in zip(u, l):
result += '%s%s' % (cha, chb)
print result
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'