it-swarm.com.de

Warum funktionieren x, y = zip (* zip (a, b)) in Python?

OK, ich liebe die Zip()-Funktion von Python. Verwenden Sie es die ganze Zeit, es ist brillant. Hin und wieder möchte ich das Gegenteil von Zip() tun, denke "Ich wusste, wie man das macht", dann Google Python entpacken und dann daran denken, dass man mit diesem magischen * eine gezippte Liste von Tupeln entpackt. So was:

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)

Was zu Hölle ist hier los? Was macht das magische Sternchen? Wo sonst kann es angewendet werden und welche anderen erstaunlichen Dinge in Python sind so geheimnisvoll und schwer zu googeln?

73
Mike Dewar

Das Sternchen in Python ist im Python-Lernprogramm unter Argumentlisten entpacken dokumentiert.

38

Das Sternchen führt apply aus (wie in LISP und Scheme bekannt). Im Grunde nimmt es Ihre Liste und ruft die Funktion mit dem Inhalt dieser Liste als Argumente auf.

18

Es ist auch nützlich für mehrere Argumente:

def foo(*args):
  print args

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

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

Und Sie können ein Doppelsternchen für Schlüsselwortargumente und Wörterbücher verwenden:

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}

Und natürlich können Sie diese kombinieren:

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

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

Ziemlich ordentlich und nützliche Sachen.

8
bcherry

Es funktioniert nicht immer:

>>> 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

Hoppla! Ich denke, es braucht einen Schädel, um die Arbeit zu erschrecken:

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

In Python3 denke ich, du brauchst

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

da Zip jetzt eine Generatorfunktion zurückgibt, die nicht False-y ist.

6
BenAnhalt

Ich bin extrem neu in Python, also hat mich das erst vor kurzem in die Knie gezwungen, aber es musste mehr damit zu tun haben, wie das Beispiel dargestellt wurde und was hervorgehoben wurde.

Was mir beim Verstehen des Zip-Beispiels Probleme bereitete, war die Asymmetrie bei der Handhabung der Zip-Rückgabewerte. Das heißt, wenn Zip zum ersten Mal aufgerufen wird, wird der Rückgabewert einer einzelnen Variablen zugewiesen, wodurch eine Listenreferenz erstellt wird (die die erstellte Tupel-Liste enthält). Beim zweiten Aufruf wird die Fähigkeit von Python genutzt, einen Listenwert (oder eine Sammlung?) Automatisch in mehrere Variablenreferenzen zu entpacken, wobei jede Referenz der einzelne Tuple ist. Wenn jemand mit der Funktionsweise von Python nicht vertraut ist, ist es einfacher, sich zu verlieren, was tatsächlich passiert.

>>> 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))) ist genau dann wahr, wenn die beiden folgenden Aussagen wahr sind:

  • x und y haben die gleiche Länge
  • x und y sind Tupel

Ein guter Weg, um zu verstehen, was vor sich geht, ist, bei jedem Schritt zu drucken:

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))))

Welche Ausgänge:

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')]

Grundsätzlich passiert folgendes:

  1. Elemente aus x und y werden entsprechend ihren jeweiligen Indizes gepaart.
  2. Paare werden zu 3 verschiedenen Objekten (Tupeln) ausgepackt
  3. Paare werden an Zip übergeben, das wiederum alle Elemente anhand von Indizes koppelt:
    • die ersten Elemente aller Eingaben werden gepaart: (1, 2, 3)
    • die zweiten Elemente aller Eingaben werden gepaart: ('a', 'b', 'c')

Jetzt können Sie verstehen, warum (x, y) == Tuple(Zip(*Zip(x,y))) in diesem Fall falsch ist:

  • da y länger als x ist, hat der erste Zip-Vorgang das zusätzliche Element aus y entfernt (da es nicht gepaart werden konnte). Diese Änderung wird offensichtlich beim zweiten Zip-Vorgang erneut durchgeführt
  • typen unterscheiden sich, zu Beginn hatten wir zwei Listen, jetzt haben wir zwei Tupel, da Zip Elemente in Tupeln und nicht in Listen paart

Wenn Sie nicht zu 100% sicher sind, wie Zip funktioniert, habe ich hier eine Antwort auf diese Frage geschrieben: Entpacken und der Operator *

0
cglacet

Nachtrag zur Antwort von @ bcherry:

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

Es funktioniert also nicht nur mit Schlüsselwortargumenten (in diesem strengen Sinn ), sondern auch mit benannten Argumenten (aka positional -Argumenten).

0
Evgeni Sergeev