s = [1,2,3,4,5,6,7,8,9]
n = 3
zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)]
Comment ça zip(*[iter(s)]*n)
marche? À quoi ressemblerait-il s'il était écrit avec un code plus détaillé?
s = [1,2,3,4,5,6,7,8,9]
n = 3
zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)]
Comment ça zip(*[iter(s)]*n)
marche? À quoi ressemblerait-il s'il était écrit avec un code plus détaillé?
Réponses:
iter()
est un itérateur sur une séquence. [x] * n
produit une liste contenant la n
quantité de x
, c'est-à-dire une liste de longueur n
, où se trouve chaque élément x
. *arg
décompresse une séquence en arguments pour un appel de fonction. Par conséquent, vous passez le même itérateur 3 fois à zip()
, et il extrait un élément de l'itérateur à chaque fois.
x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
yield
s (= return
s) un élément, vous pouvez imaginer cet élément comme "consommé". Ainsi, la prochaine fois que l'itérateur sera appelé, il produira le prochain élément "non consommé".
Les autres bonnes réponses et commentaires expliquent bien les rôles du déballage des arguments et de zip () .
Comme le disent Ignacio et ujukatzel , vous passez à zip()
trois références au même itérateur et faites zip()
3-tuples des entiers - dans l'ordre - de chaque référence à l'itérateur:
1,2,3,4,5,6,7,8,9 1,2,3,4,5,6,7,8,9 1,2,3,4,5,6,7,8,9
^ ^ ^
^ ^ ^
^ ^ ^
Et puisque vous demandez un exemple de code plus détaillé:
chunk_size = 3
L = [1,2,3,4,5,6,7,8,9]
# iterate over L in steps of 3
for start in range(0,len(L),chunk_size): # xrange() in 2.x; range() in 3.x
end = start + chunk_size
print L[start:end] # three-item chunks
Suivre les valeurs de start
et end
:
[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]
FWIW, vous pouvez obtenir le même résultat map()
avec un argument initial de None
:
>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
Pour plus d'informations sur zip()
et map()
: http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/
Je pense qu'une chose qui a manqué dans toutes les réponses (probablement évidente pour ceux qui connaissent les itérateurs) mais pas si évidente pour les autres est -
Puisque nous avons le même itérateur, il est consommé et les éléments restants sont utilisés par le zip. Donc, si nous avons simplement utilisé la liste et non l'iter par exemple.
l = range(9)
zip(*([l]*3)) # note: not an iter here, the lists are not emptied as we iterate
# output
[(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8)]
En utilisant l'itérateur, affiche les valeurs et ne garde que la disponibilité, donc pour zip, une fois que 0 est consommé, 1 est disponible, puis 2 et ainsi de suite. Une chose très subtile, mais assez intelligente !!!
iter(s)
renvoie un itérateur pour s.
[iter(s)]*n
fait une liste de n fois le même itérateur pour s.
Ainsi, en faisant zip(*[iter(s)]*n)
, il extrait un élément des trois itérateurs de la liste dans l'ordre. Puisque tous les itérateurs sont le même objet, il regroupe simplement la liste en morceaux de n
.
Un conseil pour utiliser zip de cette façon. Cela tronquera votre liste si sa longueur n'est pas également divisible. Pour contourner ce problème, vous pouvez utiliser itertools.izip_longest si vous pouvez accepter les valeurs de remplissage. Ou vous pouvez utiliser quelque chose comme ceci:
def n_split(iterable, n):
num_extra = len(iterable) % n
zipped = zip(*[iter(iterable)] * n)
return zipped if not num_extra else zipped + [iterable[-num_extra:], ]
Usage:
for ints in n_split(range(1,12), 3):
print ', '.join([str(i) for i in ints])
Impressions:
1, 2, 3
4, 5, 6
7, 8, 9
10, 11
itertools
recettes: docs.python.org/2/library/itertools.html#recipes grouper
. Pas besoin de réinventer la roue
Il est probablement plus facile de voir ce qui se passe dans l'interpréteur python ou ipython
avec n = 2
:
In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]
Donc, nous avons une liste de deux itérateurs qui pointent vers le même objet itérateur. Rappelez-vous que iter
sur un objet renvoie un objet itérateur et dans ce scénario, c'est le même itérateur deux fois en raison du *2
sucre syntaxique python. Les itérateurs ne fonctionnent également qu'une seule fois.
De plus, zip
prend n'importe quel nombre d'itérables (les séquences sont itérables ) et crée un tuple à partir du i'ème élément de chacune des séquences d'entrée. Puisque les deux itérateurs sont identiques dans notre cas, zip déplace le même itérateur deux fois pour chaque tuple à 2 éléments de sortie.
In [41]: help(zip)
Help on built-in function zip in module __builtin__:
zip(...)
zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]
Return a list of tuples, where each tuple contains the i-th element
from each of the argument sequences. The returned list is truncated
in length to the length of the shortest argument sequence.
L' opérateur unpacking ( *
) garantit que les itérateurs s'exécutent jusqu'à l'épuisement, ce qui dans ce cas est jusqu'à ce qu'il n'y ait pas assez d'entrée pour créer un tuple à 2 éléments.
Cela peut être étendu à n'importe quelle valeur de n
et zip(*[iter(s)]*n)
fonctionne comme décrit.
*
est simplement pratique pour dupliquer un objet. Essayez-le avec des scalaires puis avec des listes. Essayez aussi print(*zip(*[iter("ABCDEFG")]*2))
vs print(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")]))
. Ensuite, commencez à découper les deux en étapes plus petites pour voir quels sont réellement les objets itérateur dans les deux instructions.