usepolt
sequence unpacking

Diese Revision steht im Python-Forum zur Disskusion für große Änderungen bitte dort ansprechen. Auch Kritik und Verbesserungsvorschläge dürfen gern und viel geäußert werden. Kleinere Änderungen, die den Inhalt erhalten dürfen jederzeit vorgenommen werden.


Es ist möglich mehreren Namen je einen Wert zuzuweisen, dies geschieht gleichzeitig. Das sogenannte sequence unpacking (Sequenz auspacken). Im folgenden Beispiel werden den Namen a und b die Werte 0 und 1 zugewiesen. Soweit noch keine Hexerei.

   1 >>> a, b = 0, 1
   2 >>> a
   3 0
   4 >>> b
   5 1
   6 >>>

kleines Experiment

Leonardo Fibonacci

Anhand der Fibonacci-Folge wollen wir ein wenig mit den Mehrfachzuweisungen spielen. Leonardo Fibonacci hat eine unendlich lange Zahlenreihe beschrieben in der jede Folgezahl eine Addition der beiden vorherigen Zahlen darstellt. Man beginnt mit den Zahlen 0 und 1, durch Addition wird die Zahlenreihe um 1 erweitert (0 + 1 = 1). Der Zahlenreihe wird jetzt eine 2 angehängt, da die letzten beiden Zahlen der bestehenden Reihe 1 und 1 sind. Fibonacci musste das noch mit Papier und Stift erledigen, wir haben das Glück Python nutzen zu dürfen.

   1 >>> a, b = 0, 1
   2 >>> while a < 20 :
   3 ...     print(a)
   4 ...     a, b = b, a+b
   5 ...
   6 0
   7 1
   8 1
   9 2
  10 3
  11 5
  12 8
  13 13
  14 >>>

Wie schon erwähnt erhalten wir mit der ersten Code-Zeile zwei Namen, a enthält die Zahl 0 und b die Zahl 1. Nun folgt eine [[while]] Schleife, sie wird so lange ausgeführt wie a kleiner als 20 ist. a ist unsere Fibonacci-Zahl sie wird mit dem print Befehl ausgegeben. Jetzt folgt der Teil der dafür sorgt das unsere while Schleife nicht unendlich läuft, im ersten Durchgang der Schleife...

Bei Mehrfachzuweisungen werden zuerst alle Werte auf der Rechten Seite ausgewertet. Im Klartext lautet die Zeile also a, b = 1, 1.

(nicht) Nachgedacht

Gut, Mehrfachauswahl hin oder her. Wenn man pro Namenszuweisung aber eine Zeile Nutzt, ist der Code doch viel lesbarer. Also schnell mal was zusammen schreiben.

   1 >>> a = 0
   2 >>> b = 1
   3 >>> while a < 20 :
   4 ...     print(a)
   5 ...     a = b
   6 ...     b = a+b
   7 ...
   8 0
   9 1
  10 2
  11 4
  12 8
  13 16
  14 >>>

Irgendwas stimmt da nicht, ab 1 werden die Zahlen verdoppelt. Kein Problem, dann weisen wir in der Schleife zuerst b und dann a zu.

   1 >>> a = 0
   2 >>> b = 1
   3 >>> while a < 20 :
   4 ...     print(a)
   5 ...     b = a+b
   6 ...     a = b
   7 ...
   8 0
   9 1
  10 2
  11 4
  12 8
  13 16
  14 >>>

Klären wir mal auf was hier passiert ist. Das Problem ist das die beiden Wertzuweisungen nicht mehr gleichzeitig erfolgen sondern nacheinander. In unserem ersten Versuch wird...

Im zweiten Durchgang erhält a den Wert von b, also 2. a und b in der Summe bringen 4. Im zweiten Versuch verhält es sich ähnlich, auch wenn die Wertezuweisungen vertauscht werden. Starten wir einen letzten Versuch. Diesmal mit einem dritten Namen.

   1 >>> a = 0
   2 >>> b = 1
   3 >>> while a < 20 :
   4 ...     print(a)
   5 ...     c = a + b
   6 ...     a = b
   7 ...     b = c
   8 ...
   9 0
  10 1
  11 1
  12 2
  13 3
  14 5
  15 8
  16 13
  17 >>>

Die Ausgabe liefert die geforderte Finacci-Folge. Was ist hier passiert?

Wir kommen also zum Schluss, dass Pseudo-Mehrfachzuweisungen möglich sind. Wirklich lesbarer ist der Code aber nicht, hinzu kommt das wir aus einer Zeile Code drei gemacht haben.

Erweiterte Definition

Sehen wir uns nochmal das erste Beispiel an.

   1 >>> a, b = 0, 1

Auf der rechten Seite muss immer ein Objekt vom Typ iterable, oder eine Funktion die ein solches Objekt liefert, stehen. Ist das nicht der Fall wird mit einem TypeError abgebrochen. Das Objekt muss exakt so viele Elemente wie auf der Linken Seite Namen stehen liefern, anderen falls wird mit einem ValueError abgebrochen. Ob ein Objekt iterierbar ist lässt sich zum Beispiel wie folgt lösen.

  1.    1 >>> import collections
       2 >>> isinstance(2, collections.Iterable)
       3 False
       4 >>> isinstance("string", collections.Iterable)
       5 True
       6 >>> isinstance(("we",2), collections.Iterable)
       7 True
       8 >>>
    
  2.    1 >>> def is_iterable(iterable) :
       2 ...     try :
       3 ...             iter(iterable)
       4 ...             return True
       5 ...     except TypeError :
       6 ...             return False
       7 ...
       8 >>> is_iterable(2)
       9 False
      10 >>> is_iterable("sdasdas")
      11 True
      12 >>> is_iterable((1,0))
      13 True
      14 >>>
    

    Siehe hierzu try...except.

Beispiele

  1.    1 >>> a, b = range(2)
       2 >>> a
       3 0
       4 >>> b
       5 1
       6 >>>
    

    Dies stellt ein Äquivalent zu unserem Beispiel a, b = 0, 1 dar. range(2) liefert das geforderte iterable.

  2.    1 >>> a, b = 'df'
       2 >>> a
       3 'd'
       4 >>> b
       5 'f'
       6 >>>
    
    Auch strings (Zeichenketten) sind iterierbar.

usepolt/sequence unpacking (last edited 2010-04-23 15:11:57 by usepolt)