Comment faire un for
boucle ou une liste de compréhension pour que chaque itération me donne deux éléments?
l = [1,2,3,4,5,6]
for i,k in ???:
print str(i), '+', str(k), '=', str(i+k)
Production:
1+2=3
3+4=7
5+6=11
Comment faire un for
boucle ou une liste de compréhension pour que chaque itération me donne deux éléments?
l = [1,2,3,4,5,6]
for i,k in ???:
print str(i), '+', str(k), '=', str(i+k)
Production:
1+2=3
3+4=7
5+6=11
Réponses:
Vous avez besoin d'une pairwise()
(ou grouped()
) implémentation.
Pour Python 2:
from itertools import izip
def pairwise(iterable):
"s -> (s0, s1), (s2, s3), (s4, s5), ..."
a = iter(iterable)
return izip(a, a)
for x, y in pairwise(l):
print "%d + %d = %d" % (x, y, x + y)
Ou, plus généralement:
from itertools import izip
def grouped(iterable, n):
"s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."
return izip(*[iter(iterable)]*n)
for x, y in grouped(l, 2):
print "%d + %d = %d" % (x, y, x + y)
Dans Python 3, vous pouvez remplacer izip
par la zip()
fonction intégrée et supprimer leimport
.
Tout crédit à Martineau pour sa réponse à ma question , j'ai trouvé que c'était très efficace car il ne fait qu'une itération sur la liste et ne crée pas de listes inutiles dans le processus.
NB : Cela ne doit pas être confondu avec la pairwise
recette dans la propre itertools
documentation de Python , qui donne s -> (s0, s1), (s1, s2), (s2, s3), ...
, comme l'a souligné @lazyr dans les commentaires.
Petit ajout pour ceux qui souhaitent faire une vérification de type avec mypy sur Python 3:
from typing import Iterable, Tuple, TypeVar
T = TypeVar("T")
def grouped(iterable: Iterable[T], n=2) -> Iterable[Tuple[T, ...]]:
"""s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), ..."""
return zip(*[iter(iterable)] * n)
s -> (s0,s1), (s1,s2), (s2, s3), ...
itertools
fonction de recette du même nom. Bien sûr, le vôtre est plus rapide ...
izip_longest()
place de izip()
. Par exemple: list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))
-> [(1, 2), (3, 0)]
. J'espère que cela t'aides.
Eh bien, vous avez besoin d'un tuple de 2 éléments, donc
data = [1,2,3,4,5,6]
for i,k in zip(data[0::2], data[1::2]):
print str(i), '+', str(k), '=', str(i+k)
Où:
data[0::2]
signifie créer un sous-ensemble d'éléments (index % 2 == 0)
zip(x,y)
crée une collection de tuple à partir des collections x et y des mêmes éléments d'index.for i, j, k in zip(data[0::3], data[1::3], data[2::3]):
import
n'en fait pas partie.
>>> l = [1,2,3,4,5,6]
>>> zip(l,l[1:])
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
>>> zip(l,l[1:])[::2]
[(1, 2), (3, 4), (5, 6)]
>>> [a+b for a,b in zip(l,l[1:])[::2]]
[3, 7, 11]
>>> ["%d + %d = %d" % (a,b,a+b) for a,b in zip(l,l[1:])[::2]]
['1 + 2 = 3', '3 + 4 = 7', '5 + 6 = 11']
zip
renvoie un zip
objet en Python 3, qui n'est pas indexable. Il doit être converti en une séquence ( list
, tuple
, etc.) en premier lieu , mais « ne fonctionne pas » est un peu exagéré.
Une solution simple.
l = [1, 2, 3, 4, 5, 6] pour i dans la plage (0, len (l), 2): afficher str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])
((l[i], l[i+1])for i in range(0, len(l), 2))
pour un générateur, peut être facilement modifié pour des tuples plus longs.
Bien que toutes les réponses utilisées zip
soient correctes, je trouve que l'implémentation de la fonctionnalité vous-même conduit à un code plus lisible:
def pairwise(it):
it = iter(it)
while True:
try:
yield next(it), next(it)
except StopIteration:
# no more elements in the iterator
return
La it = iter(it)
partie garantit qu'il it
s'agit bien d'un itérateur, et pas seulement d'un itérable. Si it
est déjà un itérateur, cette ligne est un no-op.
Usage:
for a, b in pairwise([0, 1, 2, 3, 4, 5]):
print(a + b)
it
n'est qu'un itérateur et non un itérable. Les autres solutions semblent reposer sur la possibilité de créer deux itérateurs indépendants pour la séquence.
J'espère que ce sera une manière encore plus élégante de le faire.
a = [1,2,3,4,5,6]
zip(a[::2], a[1::2])
[(1, 2), (3, 4), (5, 6)]
Si vous êtes intéressé par les performances, j'ai fait un petit benchmark (en utilisant ma bibliothèque simple_benchmark
) pour comparer les performances des solutions et j'ai inclus une fonction de l'un de mes packages:iteration_utilities.grouper
from iteration_utilities import grouper
import matplotlib as mpl
from simple_benchmark import BenchmarkBuilder
bench = BenchmarkBuilder()
@bench.add_function()
def Johnsyweb(l):
def pairwise(iterable):
"s -> (s0, s1), (s2, s3), (s4, s5), ..."
a = iter(iterable)
return zip(a, a)
for x, y in pairwise(l):
pass
@bench.add_function()
def Margus(data):
for i, k in zip(data[0::2], data[1::2]):
pass
@bench.add_function()
def pyanon(l):
list(zip(l,l[1:]))[::2]
@bench.add_function()
def taskinoor(l):
for i in range(0, len(l), 2):
l[i], l[i+1]
@bench.add_function()
def mic_e(it):
def pairwise(it):
it = iter(it)
while True:
try:
yield next(it), next(it)
except StopIteration:
return
for a, b in pairwise(it):
pass
@bench.add_function()
def MSeifert(it):
for item1, item2 in grouper(it, 2):
pass
bench.use_random_lists_as_arguments(sizes=[2**i for i in range(1, 20)])
benchmark_result = bench.run()
mpl.rcParams['figure.figsize'] = (8, 10)
benchmark_result.plot_both(relative_to=MSeifert)
Donc, si vous voulez la solution la plus rapide sans dépendances externes, vous devriez probablement utiliser l'approche donnée par Johnysweb (au moment de la rédaction, c'est la réponse la plus votée et la plus acceptée).
Si cela ne vous dérange pas la dépendance supplémentaire alors à grouper
partir iteration_utilities
sera probablement un peu plus rapide.
Certaines des approches ont des restrictions, qui n'ont pas été discutées ici.
Par exemple, quelques solutions ne fonctionnent que pour les séquences (c'est-à-dire les listes, les chaînes, etc.), par exemple les solutions Margus / pyanon / taskinoor qui utilisent l'indexation tandis que d'autres solutions fonctionnent sur n'importe quel itérable (c'est-à-dire les séquences et générateurs, les itérateurs) comme Johnysweb / mic_e / mes solutions.
Ensuite, Johnysweb a également fourni une solution qui fonctionne pour d'autres tailles que 2 tandis que les autres réponses ne le font pas (d'accord, le iteration_utilities.grouper
permet également de définir le nombre d'éléments sur "groupe").
Ensuite, il y a aussi la question de savoir ce qui devrait se passer s'il y a un nombre impair d'éléments dans la liste. Le dernier élément doit-il être rejeté? La liste doit-elle être rembourrée pour la rendre de même taille? L'article restant doit-il être retourné comme pièce unique? L'autre réponse n'aborde pas ce point directement, mais si je n'ai rien oublié, ils suivent tous l'approche selon laquelle l'élément restant doit être rejeté (sauf pour la réponse des taskinoors - qui soulèvera en fait une exception).
Avec grouper
vous pouvez décider ce que vous voulez faire:
>>> from iteration_utilities import grouper
>>> list(grouper([1, 2, 3], 2)) # as single
[(1, 2), (3,)]
>>> list(grouper([1, 2, 3], 2, truncate=True)) # ignored
[(1, 2)]
>>> list(grouper([1, 2, 3], 2, fillvalue=None)) # padded
[(1, 2), (3, None)]
Utilisez les commandes zip
et iter
ensemble:
Je trouve cette solution iter
assez élégante:
it = iter(l)
list(zip(it, it))
# [(1, 2), (3, 4), (5, 6)]
Ce que j'ai trouvé dans la documentation zip de Python 3 .
it = iter(l)
print(*(f'{u} + {v} = {u+v}' for u, v in zip(it, it)), sep='\n')
# 1 + 2 = 3
# 3 + 4 = 7
# 5 + 6 = 11
Pour généraliser aux N
éléments à la fois:
N = 2
list(zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]
for (i, k) in zip(l[::2], l[1::2]):
print i, "+", k, "=", i+k
zip(*iterable)
renvoie un tuple avec l'élément suivant de chaque itérable.
l[::2]
renvoie le 1er, le 3ème, le 5ème, etc. élément de la liste: le premier deux-points indique que la tranche commence au début car il n'y a pas de nombre derrière, le deuxième deux-points n'est nécessaire que si vous voulez une étape dans la tranche »(dans ce cas 2).
l[1::2]
fait la même chose mais commence dans le deuxième élément des listes et renvoie donc les 2e, 4e, 6e, etc. éléments de la liste d' origine .
[number::number]
syntaxe. utile pour ceux qui n'utilisent pas souvent python
vous pouvez utiliser le package more_itertools .
import more_itertools
lst = range(1, 7)
for i, j in more_itertools.chunked(lst, 2):
print(f'{i} + {j} = {i+j}')
Avec déballage:
l = [1,2,3,4,5,6]
while l:
i, k, *l = l
print(str(i), '+', str(k), '=', str(i+k))
Pour tout le monde, cela pourrait aider, voici une solution à un problème similaire mais avec des paires qui se chevauchent (au lieu de paires s'excluant mutuellement).
Dans la documentation Python itertools :
from itertools import izip
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return izip(a, b)
Ou, plus généralement:
from itertools import izip
def groupwise(iterable, n=2):
"s -> (s0,s1,...,sn-1), (s1,s2,...,sn), (s2,s3,...,sn+1), ..."
t = tee(iterable, n)
for i in range(1, n):
for j in range(0, i):
next(t[i], None)
return izip(*t)
J'ai besoin de diviser une liste par un nombre et fixé comme ça.
l = [1,2,3,4,5,6]
def divideByN(data, n):
return [data[i*n : (i+1)*n] for i in range(len(data)//n)]
>>> print(divideByN(l,2))
[[1, 2], [3, 4], [5, 6]]
>>> print(divideByN(l,3))
[[1, 2, 3], [4, 5, 6]]
Il y a plusieurs façons de procéder. Par exemple:
lst = [1,2,3,4,5,6]
[(lst[i], lst[i+1]) for i,_ in enumerate(lst[:-1])]
>>>[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
[i for i in zip(*[iter(lst)]*2)]
>>>[(1, 2), (3, 4), (5, 6)]
J'ai pensé que c'était un bon endroit pour partager ma généralisation de ceci pour n> 2, qui est juste une fenêtre coulissante sur un itérable:
def sliding_window(iterable, n):
its = [ itertools.islice(iter, i, None)
for i, iter
in enumerate(itertools.tee(iterable, n)) ]
return itertools.izip(*its)
Utilisation de la saisie pour vérifier les données à l'aide de l'outil d'analyse statique mypy :
from typing import Iterator, Any, Iterable, TypeVar, Tuple
T_ = TypeVar('T_')
Pairs_Iter = Iterator[Tuple[T_, T_]]
def legs(iterable: Iterator[T_]) -> Pairs_Iter:
begin = next(iterable)
for end in iterable:
yield begin, end
begin = end
Une approche simpliste:
[(a[i],a[i+1]) for i in range(0,len(a),2)]
ceci est utile si votre tableau est un et que vous souhaitez l'itérer par paires. Pour itérer sur des triplets ou plus, il suffit de changer la commande d'étape "range", par exemple:
[(a[i],a[i+1],a[i+2]) for i in range(0,len(a),3)]
(vous devez gérer les valeurs excédentaires si la longueur de votre tableau et le pas ne correspondent pas)
from itertools import tee
def pairwise(iterable):
a = iter(iterable)
for i in a:
try:
yield (i, next(a))
except StopIteration:
yield(i, None)
for i in pairwise([3, 7, 8, 9, 90, 900]):
print(i)
Production:
(3, 7)
(8, 9)
(90, 900)
>
Ici, nous pouvons avoir une alt_elem
méthode qui peut s'adapter à votre boucle for.
def alt_elem(list, index=2):
for i, elem in enumerate(list, start=1):
if not i % index:
yield tuple(list[i-index:i])
a = range(10)
for index in [2, 3, 4]:
print("With index: {0}".format(index))
for i in alt_elem(a, index):
print(i)
Production:
With index: 2
(0, 1)
(2, 3)
(4, 5)
(6, 7)
(8, 9)
With index: 3
(0, 1, 2)
(3, 4, 5)
(6, 7, 8)
With index: 4
(0, 1, 2, 3)
(4, 5, 6, 7)
Remarque: La solution ci-dessus peut ne pas être efficace compte tenu des opérations effectuées dans func.