Quelle est la manière la plus pythonique de mailler deux cordes ensemble?
Par exemple:
Contribution:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Production:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Quelle est la manière la plus pythonique de mailler deux cordes ensemble?
Par exemple:
Contribution:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Production:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Réponses:
Pour moi, la manière la plus pythonique * est la suivante qui fait à peu près la même chose mais utilise l' +
opérateur pour concaténer les caractères individuels dans chaque chaîne:
res = "".join(i + j for i, j in zip(u, l))
print(res)
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
C'est également plus rapide que d'utiliser deux join()
appels:
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
Des approches plus rapides existent, mais elles obscurcissent souvent le code.
Remarque: Si les deux chaînes d'entrée ne sont pas de la même longueur, la plus longue sera tronquée car l' zip
itération s'arrête à la fin de la chaîne la plus courte. Dans ce cas, au lieu d' zip
un, il faut utiliser zip_longest
( izip_longest
en Python 2) du itertools
module pour s'assurer que les deux chaînes sont complètement épuisées.
* Pour reprendre une citation du Zen of Python : la lisibilité compte .
Pythonic = lisibilité pour moi; i + j
est simplement analysé visuellement plus facilement, du moins pour mes yeux.
"".join([i + j for i, j in zip(l1, l2)])
et ce sera certainement le plus rapide
"".join(map("".join, zip(l1, l2)))
est encore plus rapide, mais pas nécessairement plus pythonique.
Autrement:
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
print(''.join(res))
Production:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
On dirait que c'est plus rapide:
%%timeit
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
''.join(res)
100000 loops, best of 3: 4.75 µs per loop
que la solution la plus rapide à ce jour:
%timeit "".join(list(chain.from_iterable(zip(u, l))))
100000 loops, best of 3: 6.52 µs per loop
Aussi pour les plus grosses cordes:
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))
Production:
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))
Production:
AaBbCcDdEeFfGgHhIiJjKkLlMNOPQRSTUVWXYZ
Avec join()
et zip()
.
>>> ''.join(''.join(item) for item in zip(u,l))
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
''.join(itertools.chain.from_iterable(zip(u, l)))
zip
s'arrête lorsque la liste plus courte a été entièrement itérée.
itertools.zip_longest
peut être utilisé si cela devient un problème.
Sur Python 2, le moyen de loin le plus rapide de faire les choses, à ~ 3x la vitesse de découpage de liste pour les petites chaînes et ~ 30x pour les longues, est
res = bytearray(len(u) * 2)
res[::2] = u
res[1::2] = l
str(res)
Cela ne fonctionnerait pas sur Python 3, cependant. Vous pouvez implémenter quelque chose comme
res = bytearray(len(u) * 2)
res[::2] = u.encode("ascii")
res[1::2] = l.encode("ascii")
res.decode("ascii")
mais à ce moment-là, vous avez déjà perdu les gains par rapport au découpage de liste pour les petites chaînes (c'est toujours 20x la vitesse pour les longues chaînes) et cela ne fonctionne même pas encore pour les caractères non ASCII.
FWIW, si vous êtes en train de faire cela sur des chaînes massives et ont besoin de chaque cycle, et pour une raison quelconque doivent utiliser des chaînes Python ... voici comment faire:
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")
Un boîtier spécial, le cas courant des types plus petits, aidera également. FWIW, c'est seulement 3 fois la vitesse de découpage de liste pour les longues chaînes et un facteur de 4 à 5 plus lent pour les petites chaînes.
Quoi qu'il en soit, je préfère les join
solutions, mais comme les horaires ont été mentionnés ailleurs, j'ai pensé que je pourrais aussi bien participer.
Si vous voulez le moyen le plus rapide, vous pouvez combiner itertools avec 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
Mais combiner izip
et chain.from_iterable
c'est encore plus rapide
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
Il existe également une différence substantielle entre
chain(*
et chain.from_iterable(...
.
In [5]: timeit "".join(chain(*izip(l1, l2)))
1 loops, best of 3: 212 ms per loop
Il n'y a pas de générateur avec jointure, en transmettre un sera toujours plus lent car python construira d'abord une liste en utilisant le contenu car il effectue deux passes sur les données, un pour déterminer la taille nécessaire et un pour le faire. la jointure qui ne serait pas possible avec un générateur:
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.
*/
De plus, si vous avez des chaînes de longueur différente et que vous ne voulez pas perdre de données, vous pouvez utiliser 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'
Pour python 3, il s'appelle zip_longest
Mais pour python2, la suggestion de veedrac est de loin la plus rapide:
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
?? est inutile
"".join(list(...))
give me 6.715280318699769 and timeit the "".join(starmap(...))
give me 6.46332361384313
"".join(list(starmap(add, izip(l1,l2))))
est plus lent que "".join(starmap(add, izip(l1,l2)))
. J'exécute le test sur ma machine en python 2.7.11 et en python 3.5.1 même dans la console virtuelle de www.python.org avec python 3.4.3 et tous disent la même chose et je l'exécute plusieurs fois et toujours le same
Vous pouvez également le faire en utilisant map
et operator.add
:
from operator import add
u = 'AAAAA'
l = 'aaaaa'
s = "".join(map(add, u, l))
Sortie :
'AaAaAaAaAa'
Ce que fait la carte, c'est qu'elle prend chaque élément du premier itérable u
et les premiers éléments du deuxième itérable l
et applique la fonction fournie comme premier argument add
. Ensuite, rejoignez-les simplement.
Un grand nombre de ces suggestions supposent que les chaînes sont de longueur égale. Peut-être que cela couvre tous les cas d'utilisation raisonnables, mais au moins pour moi, il semble que vous souhaitiez peut-être aussi accueillir des chaînes de longueurs différentes. Ou suis-je le seul à penser que le maillage devrait fonctionner un peu comme ceci:
u = "foobar"
l = "baz"
mesh(u,l) = "fboaozbar"
Une façon de procéder serait la suivante:
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:]])
J'aime utiliser deux for
s, les noms de variables peuvent donner un indice / rappel de ce qui se passe:
"".join(char for pair in zip(u,l) for char in pair)
C'est un peu un-pythonique de ne pas considérer la réponse de compréhension à double liste ici, pour gérer n chaînes avec un effort O (1):
"".join(c for cs in itertools.zip_longest(*all_strings) for c in cs)
où all_strings
est une liste des chaînes que vous souhaitez entrelacer. Dans votre cas all_strings = [u, l]
,. Un exemple d'utilisation complet ressemblerait à ceci:
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'
Comme beaucoup de réponses, le plus rapide? Probablement pas, mais simple et flexible. De plus, sans trop de complexité supplémentaire, c'est légèrement plus rapide que la réponse acceptée (en général, l'ajout de chaînes est un peu lent en 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
Potentiellement plus rapide et plus court que la principale solution actuelle:
from itertools import chain
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
res = "".join(chain(*zip(u, l)))
La stratégie en termes de vitesse consiste à faire autant que possible au niveau C. Même correctif de zip_longest () pour les chaînes inégales et il sortirait du même module que chain (), donc je ne peux pas me donner trop de points!
Autres solutions que j'ai trouvées en cours de route:
res = "".join(u[x] + l[x] for x in range(len(u)))
res = "".join(k + l[i] for i, k in enumerate(u))
Vous pouvez utiliser 1iteration_utilities.roundrobin
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
from iteration_utilities import roundrobin
''.join(roundrobin(u, l))
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
ou la ManyIterables
classe du même package:
from iteration_utilities import ManyIterables
ManyIterables(u, l).roundrobin().as_string()
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
1 Ceci est d'une bibliothèque tierce , je l' ai écrit: iteration_utilities
.