web-dev-qa-db-fra.com

Pourquoi x, y = Zip (* Zip (a, b)) fonctionne-t-il en Python?

OK j'aime la fonction Zip() de Python. Utilisez-le tout le temps, c'est génial. De temps en temps, je veux faire le contraire de Zip(), penser «j’ai su comment faire cela», puis google python unzip, puis rappeler que l’on utilise ce * magique pour décompresser une liste de tuples zippée. Comme ça:

x = [1,2,3]
y = [4,5,6]
zipped = Zip(x,y)
unzipped_x, unzipped_y = Zip(*zipped)
unzipped_x
    Out[30]: (1, 2, 3)
unzipped_y
    Out[31]: (4, 5, 6)

Qu'est-ce qui se passe? Que fait cet astérisque magique? Où peut-il être appliqué et quelles autres choses incroyables et impressionnantes dans Python sont si mystérieuses et difficiles à rechercher sur Google?

73
Mike Dewar

L'astérisque en Python est documenté dans le didacticiel Python, sous - Lister les listes d'arguments .

38
Daniel Stutzbach

L'astérisque exécute apply (comme il est connu dans LISP et Scheme). Fondamentalement, il prend votre liste et appelle la fonction avec le contenu de cette liste comme arguments.

18
Chris Jester-Young

C'est aussi utile pour plusieurs arguments:

def foo(*args):
  print args

foo(1, 2, 3) # (1, 2, 3)

# also legal
t = (1, 2, 3)
foo(*t) # (1, 2, 3)

De plus, vous pouvez utiliser un double astérisque pour les arguments de mots clés et les dictionnaires:

def foo(**kwargs):
   print kwargs

foo(a=1, b=2) # {'a': 1, 'b': 2}

# also legal
d = {"a": 1, "b": 2}
foo(**d) # {'a': 1, 'b': 2}

Et bien sûr, vous pouvez combiner ces éléments:

def foo(*args, **kwargs):
   print args, kwargs

foo(1, 2, a=3, b=4) # (1, 2) {'a': 3, 'b': 4}

Des choses très soignées et utiles.

8
bcherry

Ça ne marche pas toujours:

>>> x = []
>>> y = []
>>> zipped = Zip(x, y)
>>> unzipped_x, unzipped_y = Zip(*zipped)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: need more than 0 values to unpack

Oops! Je pense qu'il a besoin d'un crâne pour l'effrayer en travaillant:

>>> unzipped_x, unzipped_y = Zip(*zipped) or ([], [])
>>> unzipped_x
[]
>>> unzipped_y
[]

En python3 je pense que tu as besoin

>>> unzipped_x, unzipped_y = Tuple(Zip(*zipped)) or ([], [])

puisque Zip retourne maintenant une fonction génératrice qui n'est pas False-y.

6
BenAnhalt

Je suis extrêmement nouveau en Python, alors cela m’a tout récemment fait trébucher, mais il fallait en faire plus avec la façon dont l’exemple était présenté et ce qui était souligné.

Ce qui m'a posé des problèmes pour comprendre l'exemple Zip, c'est l'asymétrie dans le traitement des valeurs de retour d'appel Zip. C'est-à-dire que lorsque Zip est appelé pour la première fois, la valeur renvoyée est affectée à une seule variable, créant ainsi une référence à la liste (contenant la liste de Tuple créée). Dans le deuxième appel, il exploite la capacité de Python à décompresser automatiquement une liste (ou une collection?) En renvoyant une valeur renvoyée dans plusieurs références de variable, chaque référence étant le tuple individuel. Si quelqu'un ne sait pas comment cela fonctionne en Python, il est plus facile de se perdre pour savoir ce qui se passe réellement.

>>> x = [1, 2, 3]
>>> y = "abc"
>>> zipped = Zip(x, y)
>>> zipped
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> z1, z2, z3 = Zip(x, y)
>>> z1
(1, 'a')
>>> z2
(2, 'b')
>>> z3
(3, 'c')
>>> rezipped = Zip(*zipped)
>>> rezipped
[(1, 2, 3), ('a', 'b', 'c')]
>>> rezipped2 = Zip(z1, z2, z3)
>>> rezipped == rezipped2
True
2
user3447701

(x, y) == Tuple(Zip(*Zip(x,y))) est vrai si et seulement si les deux déclarations suivantes sont vraies:

  • x et y ont la même longueur
  • x et y sont des n-uplets

Un bon moyen de comprendre ce qui se passe est d’imprimer à chaque étape:

x = [1, 2, 3]
y = ["a", "b", "c", "d"]

print("1) x, y = ", x, y)
print("2) Zip(x, y) = ", list(Zip(x, y)))
print("3) *Zip(x, y) = ", *Zip(x, y))
print("4) Zip(*Zip(x,y)) = ", list(Zip(*Zip(x,y))))

Quelles sorties:

1) x, y =            [1, 2, 3] ['a', 'b', 'c', 'd']
2) Zip(x, y) =       [(1, 'a'), (2, 'b'), (3, 'c')]
3) *Zip(x, y) =       (1, 'a')  (2, 'b')  (3, 'c')
4) Zip(*Zip(x,y)) =  [(1, 2, 3), ('a', 'b', 'c')]

Fondamentalement, voici ce qui se passe:

  1. Les éléments de x et y sont appariés en fonction de leurs index respectifs.
  2. Les paires sont décompressées en 3 objets différents (tuples)
  3. Les paires sont passées à Zip, qui à nouveau, appareille tous les éléments en fonction des index:
    • les premiers éléments de toutes les entrées sont appariés: (1, 2, 3)
    • les deuxièmes éléments de toutes les entrées sont appariés: ('a', 'b', 'c')

Vous pouvez maintenant comprendre pourquoi (x, y) == Tuple(Zip(*Zip(x,y))) est faux dans ce cas:

  • étant donné que y est plus long que x, la première opération Zip a supprimé l'élément supplémentaire de y (car il ne pouvait pas être apparié), ce changement est évidemment répercuté sur la deuxième opération de compression.
  • les types diffèrent, au début nous avions deux listes, maintenant nous avons deux n-uplets car Zip fait la paire d'éléments dans les n-uplets et non dans les listes

Si vous n'êtes pas sûr à 100% de comprendre le fonctionnement de Zip, j’ai répondu ici à cette question: Décompression et opérateur *

0
cglacet

Addendum à la réponse de @ bcherry:

>>> def f(a2,a1):
...  print a2, a1
... 
>>> d = {'a1': 111, 'a2': 222}
>>> f(**d)
222 111

Cela fonctionne donc non seulement avec les arguments de mots clés (dans ce sens strict ), mais aussi avec les arguments nommés (aka positional arguments).

0
Evgeni Sergeev