List Comprehensions

Grundlegendes

List Comprehensions bieten eine kurze und prägnante Möglichkeit, Listen zu erstellen. Wenn wir zum Beispiel eine Liste von Quadratzahlen erzeugen wollen, so ist eine der Möglichkeiten folgende

In [2]:
quadratzahlen = []
for x in range(10):
    quadratzahlen.append(x**2)
print(quadratzahlen)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Zu beachten ist, das der obige Code eine Variable x erzeugt (oder überschreibt) die auch nach dem Ende der Schleife weiterhin existiert. Eine weitere Möglichkeit ist daher die Erzeugung der Liste von Quadratzahlen ohne solche Seiteneffekte, wenn man folgendermaßen vorgeht:

In [3]:
quadratzahlen = list(map(lambda x: x**2, range(10)))
print(quadratzahlen)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

oder, äquivalent hierzu die prägnantere und lesbarere List Comprehension-Kurzform:

In [4]:
quadratzahlen = [x**2 for x in range(10)]
print(quadratzahlen)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
In [5]:
a = range(5)
print(list(a))
b = range(5,0,-1)
print(list(b))
c = [a[i]+b[i] for i in range(len(a))]
print(c)
[0, 1, 2, 3, 4]
[5, 4, 3, 2, 1]
[5, 5, 5, 5, 5]
In [6]:
%%Mooc StringAssessment
Out[6]:

List Comprehension - 1

Gegeben sei der folgende Code


a = range(5)
b = range(5,0,-1)
c = [a[i]+b[i] for i in range(len(a))]
print(c)

Geben sie die Ausgabe des Print-Befehls vom oben aufgeführten Code an.



Allgemeine Definition einer List Comprehension

Eine List Comprehension besteht aus einem Paar eckiger Klammern mit einem Ausdruck und einer nachfolgenden for-Schleife sowie eventuellen weiteren for-Schleifen oder if-Bedingungen. Das Ergebnis ist dann eine neue Liste welche sich aus dem Kontext der for-Schleifen und if-Bedingungen ergibt.

Zum Beispiel kombiniert die folgende List Comprehension die Elemente von zwei Listen, wenn deren Werte nicht gleich sind:

In [7]:
combs = [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
print(combs)
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

Diese List Comprehension ist äquivalent zu:

In [8]:
combs = []
for x in [1,2,3]:
    for y in [3,1,4]:
        if x != y:
            combs.append((x, y))
print(combs)
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

Zu beachten ist, dass die Anordnung der for-Schleifen und if-Bedingungen in beiden Code-Stücken die gleiche ist. Wenn der Ausdruck ein Tupel ergeben soll (wie z.B. (x,y) in obigem Beispiel), dann muss dieser Ausdruck in runde Klammern gesetzt werden.

Weitere Beispiele von List Comprehensions

Vektor Multiplikation

Eine Liste kann immer auch als mehrdimensionaler Vektor angesehen werden. Im dreidimensionalen Raum ist ein Vektor daher eine Liste mit der Länge 3. Eine Liste der Länge n ist daher ein Vektor in dem n-dimensionalem Raum.

In [9]:
vektor = [-2, 0, 2]
doppelterVektor = [x*2 for x in vektor]
print(doppelterVektor)
[-4, 0, 4]

Filter um negative Zahlen auszuschliessen

In [10]:
positiveListe = [x for x in vektor if x >= 0]
print(positiveListe)
[0, 2]

Anwenden einer Funktion auf alle Elemente

In [11]:
absoluterVektor = [abs(x) for x in vektor]
print(absoluterVektor)
[2, 0, 2]

Anwenden einer Methode auf jedes Element

In [12]:
fruechte = ['  Apfel', '  Birne  ', 'Erdbeere  ']
fruechte = [frucht.strip() for frucht in fruechte]
print(fruechte)
['Apfel', 'Birne', 'Erdbeere']

Liste mit Zweier-Tupeln wie (zahl, quadratzahl)

In [13]:
quadratzahlen = [(x, x**2) for x in range(6)]
print(quadratzahlen)
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]

Tupel müssen in Klammern gesetzt werden, ansonsten wird ein Syntaxfehler geworfen

In [14]:
quadratzahlen = [x, x**2 for x in range(6)]
  File "<ipython-input-14-8ec16e966448>", line 1
    quadratzahlen = [x, x**2 for x in range(6)]
                               ^
SyntaxError: invalid syntax

Verflachen (flatten) einer Liste von Listen mit einer List Comprehension mit zwei for-Schleifen

In [15]:
listeVonVektoren = [[1,2,3], [4,5,6], [7,8,9]]
flacheListe = [zahl for element in listeVonVektoren for zahl in element]
print(flacheListe)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

List Comprehensions können komplexe Ausdrücke und verschachtelte Funktionen enthalten

In [16]:
from math import pi
piGenauigkeit = [str(round(pi, i)) for i in range(1, 6)]
print(piGenauigkeit)
['3.1', '3.14', '3.142', '3.1416', '3.14159']
In [17]:
%%Mooc StringAssessment
Out[17]:

List Comprehension - 2

Gegeben sei der folgende Code


a = [('zwei',2), ('fünf',5), ('eins',1), ('vier',4), ('drei',3)]
a.sort()
print(a)
b = [(x[1],x[0]) for x in a]
b.sort()
print(b)

Geben sie die Ausgabe des letzten Print-Befehls vom oben aufgeführten Code an.



In [18]:
%%Mooc StringAssessment
Out[18]:

List Comprehension - 3

Gegeben sei der folgende Code


a = ['eins','zwei','drei','vier','fünf']
b = [x.capitalize() for x in a]
print(b)

Geben sie die Ausgabe des Print-Befehls vom oben aufgeführten Code an.



Verschachtelte List Comprehensions

Allgemeines

Der erste Ausdruck in einer List Comprehension kann ein beliebiger Ausdruck sein, einschliesslich einer weiteren List Comprehension.

Das folgende Beispiel erstellt eine 3x4 Matrix als Liste von 3 Liste mit jeweils der Länge 4:

In [19]:
matrix = [
             [1, 2, 3, 4],
             [5, 6, 7, 8],
             [9, 10, 11, 12],
         ]

Die folgende List Comprehension vertauscht die Spalten und die Zeilen:

In [20]:
vertauschteMatrix = [[row[i] for row in matrix] for i in range(4)]
print(vertauschteMatrix)
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Wie wir in der vorangehenden Einheit gesehen haben, wird die innere (verschachtelte) List Comprehension aus dem Kontext der for-Schleife, die der inneren List Comprehension folgt, berechnet. Damit wird diese List Comprehension äquivalent der folgenden Schleife:

In [21]:
vertauschteMatrix = []
for i in range(4):
     vertauschteMatrix.append([row[i] for row in matrix])
print(vertauschteMatrix)
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

und obiger Code ist nun wiederum äquivalent dem folgenden, in dem auch die innere List Comprehension als Schleife ausformuliert wurde:

In [22]:
vertauschteMatrix = []
for i in range(4):
     # the following 3 lines implement the nested listcomp
    vertauschteSpalten = []
    for row in matrix:
        vertauschteSpalten.append(row[i])
    vertauschteMatrix.append(vertauschteSpalten)
print(vertauschteMatrix)
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Zip Funktion

Eine weitere Vereinfachung für komplexe Ausdrücke ist die Builtin-Funktion zip. Allgemein gesprochen, erstellt die Funktion zip einen Iterator (ein Objekt über das iteriert werden kann) aus den jeweiligen Elementen seiner Argumente. Die genauere Beschreibung verdeutlicht dies: die Funktion zip gibt einen Iterator aus Tupeln zurück, wobei der i.te Tuple alle i.ten Elemente der Argumente von zip enthält. Der zurückgegebene Iterator stoppt, wenn das kürzeste Argument sein Ende erreicht hat.

In [23]:
x = [1, 2, 3]
y = [4, 5, 6]
zipped = list(zip(x, y))
print(zipped)
[(1, 4), (2, 5), (3, 6)]

Die vertauschte Matrix lässt sich somit folgendermaßen berechnen:

In [24]:
vertauschteMatrix = list(zip(matrix[0],matrix[1],matrix[2]))
print(vertauschteMatrix)
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

Auspacken einer Argumenten Liste

Wenn Argumente, die an eine Funktion übergeben werden sollen, bereits in einer Liste oder einem Tupel vorliegt, dann muss diese Liste oder das Tupel ausgepackt werden, damit die Funktion entsprechend ihrer Definition korrekt mit den einzelnen, an den jeweiligen Positionen notwendigen Argumenten bedient wird. Dieses Auspacken geschieht bei einer Liste oder einem Tupel mit einem vorangestellten *-Operator. Zum Beispiel erwartet die Builtin-Funktion range getrennte Start- und Stop Argumente. Falls diese nicht separat zur Verfügung stehen, kann der *-Operator verwendet werden, um die Argumente aus einer Liste oder einem Tupel auszupacken. Im folgenden Beispiel ist erst der "normale" Aufruf von range aufgeführt und anschliessend der Aufruf mit dem *-Operator:

In [25]:
a = list(range(3, 6))
print(a)
args = [3, 6]
a = list(range(*args))
print(a)
[3, 4, 5]
[3, 4, 5]

Mit dieser Möglichkeit wird die obige Berechnung der vertauschten Matrix noch einfacher, da die Liste matrix mit dem *-Operator ausgepackt an die Funktion zip übergeben wird:

In [26]:
vertauschteMatrix = list(zip(*matrix))
print(vertauschteMatrix)
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

Verdeutlicht wird dies auch nochmal durch die Ausgabe von *matrix, welches identisch ist mit matrix[0],matrix[1],matrix[2]:

In [27]:
print(*matrix)
print(matrix[0],matrix[1],matrix[2])
[1, 2, 3, 4] [5, 6, 7, 8] [9, 10, 11, 12]
[1, 2, 3, 4] [5, 6, 7, 8] [9, 10, 11, 12]
In [28]:
%%Mooc StringAssessment
Out[28]:

Auspacken von Argument Listen

Gegeben sei der folgende Code


def quadr(a=1,b=0,c=0):
    return lambda x: a*x**2+b*x+c
args=[3,2,1]

Fangen sie das Ergebnis folgendermaßen an:

f=quadr(

Wie lautet der Aufruf der Funktion quadr, wenn die Argumente a,b,c in der Liste args übergeben werden sollen ?



In [29]:
%%Mooc Video
Out[29]:

Weitere Literatur

In [30]:
%%Mooc WebReference

List Comprehensions

https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions

Hinweis: Beschreibung von List Comprehensions

In [31]:
%%Mooc WebReference

Verschachtelte List Comprehensions

https://docs.python.org/3/tutorial/datastructures.html#nested-list-comprehensions

Hinweis: Beschreibung der verschachtelten List Comprehensions

In [32]:
%%Mooc WebReference

Builtin Funktion zip

https://docs.python.org/3/library/functions.html#zip

Hinweis: Beschreibung der Builtin-Funktion zip

In [33]:
%%Mooc WebReference

Auspacken von Argument Listen

https://docs.python.org/3/tutorial/controlflow.html#tut-unpacking-arguments

Hinweis: Auspacken von Listen zur Übergabe von Argumenten an Funktionen