Was ist die pythonischste Art, zwei Saiten miteinander zu verbinden?
Beispielsweise:
Eingang:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Ausgabe:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Was ist die pythonischste Art, zwei Saiten miteinander zu verbinden?
Beispielsweise:
Eingang:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Ausgabe:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Antworten:
Für mich ist der pythonischste * Weg der folgende, der so ziemlich das Gleiche tut, aber den +
Operator zum Verketten der einzelnen Zeichen in jeder Zeichenfolge verwendet:
res = "".join(i + j for i, j in zip(u, l))
print(res)
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Es ist auch schneller als zwei join()
Anrufe:
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
Es gibt schnellere Ansätze, die den Code jedoch häufig verschleiern.
Hinweis: Wenn die beiden Eingabezeichenfolgen nicht dieselbe Länge haben, wird die längere abgeschnitten, da zip
die Iteration am Ende der kürzeren Zeichenfolge stoppt. In diesem Fall statt zip
sollte man verwenden zip_longest
( izip_longest
in Python 2) aus dem itertools
Modul , um sicherzustellen , dass beide Saiten voll ausgeschöpft werden.
* Um ein Zitat aus dem Zen von Python zu erhalten : Lesbarkeit zählt .
Pythonic = Lesbarkeit für mich; i + j
wird nur visuell leichter analysiert, zumindest für meine Augen.
"".join([i + j for i, j in zip(l1, l2)])
und es wird definitiv das schnellste sein
"".join(map("".join, zip(l1, l2)))
ist noch schneller, wenn auch nicht unbedingt pythonischer.
Ein anderer Weg:
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
print(''.join(res))
Ausgabe:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Sieht so aus, als wäre es schneller:
%%timeit
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
''.join(res)
100000 loops, best of 3: 4.75 µs per loop
als die bisher schnellste Lösung:
%timeit "".join(list(chain.from_iterable(zip(u, l))))
100000 loops, best of 3: 6.52 µs per loop
Auch für die größeren Saiten:
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()
äquivalent)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))
Ausgabe:
AaBbCcDdEeFfGgHhIiJjKkLl
itertools.zip_longest(fillvalue='')
äquivalent)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))
Ausgabe:
AaBbCcDdEeFfGgHhIiJjKkLlMNOPQRSTUVWXYZ
Mit join()
und zip()
.
>>> ''.join(''.join(item) for item in zip(u,l))
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
''.join(itertools.chain.from_iterable(zip(u, l)))
zip
wenn die kürzere Liste vollständig durchlaufen wurde.
itertools.zip_longest
kann verwendet werden, wenn es ein Problem wird.
Unter Python 2 ist der weitaus schnellere Weg, Dinge zu erledigen, mit ~ 3x der Geschwindigkeit, mit der Listen für kleine Zeichenfolgen und ~ 30x für lange Zeichenfolgen geschnitten werden
res = bytearray(len(u) * 2)
res[::2] = u
res[1::2] = l
str(res)
Dies würde jedoch unter Python 3 nicht funktionieren. Sie könnten so etwas implementieren
res = bytearray(len(u) * 2)
res[::2] = u.encode("ascii")
res[1::2] = l.encode("ascii")
res.decode("ascii")
Bis dahin haben Sie jedoch bereits die Vorteile des Listen-Slicing für kleine Zeichenfolgen verloren (es ist immer noch 20-mal so schnell wie für lange Zeichenfolgen), und dies funktioniert noch nicht einmal für Nicht-ASCII-Zeichen.
FWIW, wenn Sie werden diese auf massiven Saiten zu tun und müssen in jedem Zyklus, und aus irgendeinem Grund Python - Strings verwenden ... ist hier , wie es geht:
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")
Ein spezielles Gehäuse, wie es bei kleineren Typen üblich ist, hilft ebenfalls. FWIW, dies ist nur das Dreifache der Geschwindigkeit des Listenschneidens für lange Zeichenfolgen und ein Faktor von 4 bis 5 langsamer für kleine Zeichenfolgen.
In beiden Fällen bevorzuge ich die join
Lösungen, aber da die Timings an anderer Stelle erwähnt wurden, dachte ich, ich könnte genauso gut mitmachen.
Wenn Sie den schnellsten Weg suchen , können Sie itertools kombinieren mit 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
Aber kombinieren izip
und chain.from_iterable
ist wieder schneller
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
Es gibt auch einen wesentlichen Unterschied zwischen
chain(*
und chain.from_iterable(...
.
In [5]: timeit "".join(chain(*izip(l1, l2)))
1 loops, best of 3: 212 ms per loop
Es gibt keinen Generator mit Join. Die Übergabe eines Generators wird immer langsamer, da Python zuerst eine Liste mit dem Inhalt erstellt, da zwei Durchgänge über die Daten ausgeführt werden, einer, um die erforderliche Größe zu ermitteln, und einer, um tatsächlich zu tun die Verknüpfung, die mit einem Generator nicht möglich wäre:
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.
*/
Auch wenn Sie Zeichenfolgen unterschiedlicher Länge haben und keine Daten verlieren möchten, können Sie izip_longest verwenden :
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'
Für Python 3 heißt es zip_longest
Für python2 ist der Vorschlag von veedrac jedoch bei weitem der schnellste:
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
? wird nicht benötigt
"".join(list(...))
gib mir 6.715280318699769 und timeit das "".join(starmap(...))
gib mir 6.46332361384313
"".join(list(starmap(add, izip(l1,l2))))
das langsamer ist als "".join(starmap(add, izip(l1,l2)))
. Ich führe den Test auf meinem Computer in Python 2.7.11 und in Python 3.5.1 sogar in der virtuellen Konsole von www.python.org mit Python 3.4.3 aus und alle sagen dasselbe und ich führe ihn ein paar Mal und immer aus gleiche
Sie können dies auch mit map
und tun operator.add
:
from operator import add
u = 'AAAAA'
l = 'aaaaa'
s = "".join(map(add, u, l))
Ausgabe :
'AaAaAaAaAa'
Map nimmt jedes Element aus der ersten Iterable u
und die ersten Elemente aus der zweiten Iterable l
und wendet die als erstes Argument angegebene Funktion an add
. Dann schließt sich ihnen einfach an.
Bei vielen dieser Vorschläge wird davon ausgegangen, dass die Zeichenfolgen gleich lang sind. Vielleicht deckt das alle vernünftigen Anwendungsfälle ab, aber zumindest scheint es mir, dass Sie auch Saiten unterschiedlicher Länge aufnehmen möchten. Oder bin ich der einzige, der denkt, das Netz sollte ein bisschen so funktionieren:
u = "foobar"
l = "baz"
mesh(u,l) = "fboaozbar"
Ein Weg, dies zu tun, wäre der folgende:
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:]])
Ich benutze gerne zwei for
s, die Variablennamen können einen Hinweis / eine Erinnerung geben, was los ist:
"".join(char for pair in zip(u,l) for char in pair)
Fühlt sich ein bisschen unpythonisch an, die Antwort auf das Doppellistenverständnis hier nicht zu berücksichtigen, um n Zeichenfolgen mit O (1) Aufwand zu behandeln:
"".join(c for cs in itertools.zip_longest(*all_strings) for c in cs)
Wo all_strings
ist eine Liste der Zeichenfolgen, die Sie verschachteln möchten. In Ihrem Fall all_strings = [u, l]
. Ein Beispiel für eine vollständige Verwendung würde folgendermaßen aussehen:
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'
Wie viele Antworten am schnellsten? Wahrscheinlich nicht, aber einfach und flexibel. Ohne zu viel zusätzliche Komplexität ist dies etwas schneller als die akzeptierte Antwort (im Allgemeinen ist das Hinzufügen von Zeichenfolgen in Python etwas langsam):
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
Potenziell schneller und kürzer als die derzeit führende Lösung:
from itertools import chain
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
res = "".join(chain(*zip(u, l)))
In Bezug auf die Geschwindigkeit besteht die Strategie darin, so viel wie möglich auf C-Ebene zu tun. Dieselbe zip_longest () - Korrektur für ungleichmäßige Zeichenfolgen und sie würde aus demselben Modul wie chain () stammen, kann mir also nicht zu viele Punkte geben!
Andere Lösungen, die ich mir auf dem Weg ausgedacht habe:
res = "".join(u[x] + l[x] for x in range(len(u)))
res = "".join(k + l[i] for i, k in enumerate(u))
Sie könnten 1 verwendeniteration_utilities.roundrobin
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
from iteration_utilities import roundrobin
''.join(roundrobin(u, l))
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
oder die ManyIterables
Klasse aus demselben Paket:
from iteration_utilities import ManyIterables
ManyIterables(u, l).roundrobin().as_string()
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
1 Dies ist aus einer Drittanbieter-Bibliothek, die ich geschrieben habe : iteration_utilities
.