2010-03-24 2 views
69

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(), pense "je savais comment faire ça", puis google python unzip, puis rappelez-vous que l'on utilise ce * magique pour décompresser une liste compressée de tuples. Comme ceci:Pourquoi x, y = zip (* zip (a, b)) fonctionne-t-il en Python?

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) 

Que diable se passe-t-il? Que fait cet astérisque magique? Où d'autre peut-il être appliqué et quelles autres choses géniales en Python sont si mystérieuses et difficiles à google?

+2

double: http://stackoverflow.com/questions/2233204/how-does-zipitersn-work-in-python –

+3

oh ouais. C'est exactement le problème, la recherche de stackoverflow pour 'zip (*' python ne retourne pas la question en double sur la première page, et googler pour 'python *' ou 'python zip (*' ne revient pas beaucoup parce que je suppose le '(*' est ignoré? Vous avez raison, quelqu'un d'autre a également pensé que c'était génial.Est-ce que je devrais supprimer la question? –

+1

Je ne voudrais pas le supprimer, car il se classe plus haut dans la recherche pour une raison quelconque. –

Répondre

18

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

+1

La série Python2 a encore un fonction 'apply', mais je ne pense pas qu'il y ait sont des cas d'utilisation qui ne peuvent pas être couverts par '*'. Je crois qu'il a été retiré de Python3 –

+1

@gnibbler: Confirmé. 'apply' est répertorié à l'adresse http://www.python.org/dev/peps/pep-0361/ sous la rubrique' Avertissements pour les fonctionnalités supprimées dans Py3k: ' – MatrixFrog

+2

Apply n'existe que parce que l'astérisque a été ajouté plus tard. – DasIch

8

Il est également utile pour plusieurs args:

def foo(*args): 
    print args 

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

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

Et, vous pouvez utiliser le 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:

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

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

Assez joli et utile.

6

Il ne fonctionne 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 effrayer en travail:

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

En python3 Je pense que vous avez besoin

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

depuis zip retourne maintenant une fonction de générateur qui est pas faux-y.

+0

Ou utilisez simplement un générateur 'unzipped_x = (z [0] pour z dans zippé)'. Si 'zipped' est lui-même un générateur, convertissez-le d'abord en une liste afin de pouvoir recommencer pour' unzipped_y'. Il n'y a pas de coût supplémentaire par rapport à 'zip (* zippé)' car ce dernier convertit également 'zipped' en une liste lors du déballage des arguments. –

0

Addendum à @ bcherry de réponse:

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

Il fonctionne pas seulement avec des arguments de mots-clés (en this strict sense), mais avec des arguments nommés aussi (alias position des arguments).

2

Je suis extrêmement novice en Python, donc cela m'a 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 donné des problèmes avec la compréhension de l'exemple de zip était l'asymétrie dans la gestion de la (des) valeur (s) de retour de l'appel Zip. C'est-à-dire que lorsque vous appelez zip la première fois, la valeur de retour est affectée à une seule variable, créant ainsi une référence de liste (contenant la liste de tuples créée). Dans le second appel, il tire parti de la capacité de Python à décompresser automatiquement une liste (ou une collection?) De valeurs de retour en plusieurs références de variables, chaque référence étant le tuple individuel. Si quelqu'un ne connaît pas comment cela fonctionne en Python, il est plus facile de se perdre sur 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