Listen-Abstraktion (List Comprehension)
Einführung
Wir haben im vorherigen Kapitel "Lambda-Operator, Filter, Reduce und Map" erfahren, dass Guido van Rossum Listenverständnisse Konstrukten mit Map, Filtern, Reduzieren vorzieht und Lambda. In diesem Kapitel werden wir die wesentlichen Aspekte des Listenverständnisses behandeln.
Listenverständnisse wurden mit Python 2.0 hinzugefügt. Im Wesentlichen ist es Pythons Art, eine bekannte Notation für Mengen zu implementieren, wie sie von Mathematikern verwendet wird. In der Mathematik werden die quadratischen Zahlen der natürlichen Zahlen beispielsweise durch {x2 | erzeugt x ∈ ∈} oder die Menge komplexer Ganzzahlen {(x, y) | x ∈ ∈ ∧ y ∈ ∈}.
Das Listenverständnis ist eine elegante Möglichkeit, Listen in Python zu definieren und zu erstellen. Diese Listen haben oft die Eigenschaften von Mengen, sind aber nicht unbedingt Mengen.
Das Listenverständnis ist ein vollständiger Ersatz für die Lambda-Funktion sowie die Funktionen map (), filter () und redu (). Für die meisten Menschen ist die Syntax des Listenverständnisses leichter zu verstehen.
Beispiele
Im Kapitel über Lambda und map () haben wir eine map () -Funktion entworfen, um Celsius-Werte in Fahrenheit umzuwandeln und umgekehrt. Es sieht so aus mit Listenverständnis:
Celsius = [39.2, 36.5, 37.3, 37.8]
Fahrenheit = [ ((float(9)/5)*x + 32) for x in Celsius ]
print(Fahrenheit)
Ein pythagoreisches Tripel besteht aus drei positiven ganzen Zahlen a, b und c, so dass $a^2 + b^2 = c^2$. Ein solches Tripel wird üblicherweise geschrieben (a, b, c), und das bekannteste Beispiel ist (3, 4, 5). Das folgende Listenverständnis erzeugt die pythagoreischen Tripel:
[(x,y,z) for x in range(1,30) for y in range(x,30) for z in range(y,30) if x**2 + y**2 == z**2]
Ein weiteres Beispiel: A und B seien zwei Mengen, das Kreuzprodukt (oder kartesische Produkt) von A und B, geschrieben A × B, ist die Menge aller Paare, wobei das erste Element ein Mitglied der Menge A und des zweiten Elements ist ist ein Mitglied der Menge B.
Mathematische Definition: A × B = {(a, b): a gehört zu A, b gehört zu B}. In Python ist das ganz einfach:
farben = [ "rot", "grün", "gelb", "blau" ]
sachen = [ "Haus", "Auto", "Baum" ]
farbige_sachen = [ (x,y) for x in farben for y in sachen ]
print(farbige_sachen)
Generatorverständnis
Generator-Verständnis wurde mit Python 2.6 eingeführt. Sie sind einfach wie ein Listenverständnis, aber mit Klammern - runden Klammern - anstelle von (eckigen) Klammern. Ansonsten ist die Syntax und die Arbeitsweise wie das Listenverständnis, aber ein Generatorverständnis gibt einen Generator anstelle einer Liste zurück.
x = (x **2 for x in range(20))
print(x)
x = list(x)
print(x)
kein_prim_zahl = [j for i in range(2, 8) for j in range(i*2, 100, i)]
prim_zahlen = [x for x in range(2, 100) if x not in kein_prim_zahl]
print(prim_zahlen)
Wir wollen das vorherige Beispiel in eine allgemeinere Form bringen, damit wir die Liste der Primzahlen bis zu einer beliebigen Zahl n berechnen können:
from math import sqrt
n = 100
sqrt_n = int(sqrt(n))
kein_prim_zahl = [j for i in range(2, sqrt_n+1) for j in range(i*2, n, i)]
kein_prim_zahl
Wenn wir uns den Inhalt von kein_prim_zahl ansehen, können wir sehen, dass wir ein Problem haben. Diese Liste enthält viele doppelte Einträge:
Die Lösung für dieses unerträgliche Problem ist einfacher als Sie vielleicht denken. Es geht nur darum, eckige Klammern in geschweifte Klammern umzuwandeln, oder mit anderen Worten: Wir werden das Mengenverständnis verwenden.
Verständnis festlegen
Ein Mengenverständnis ähnelt einem Listenverständnis, gibt jedoch eine Menge und keine Liste zurück. Syntaktisch verwenden wir geschweifte Klammern anstelle von eckigen Klammern, um eine Menge zu erstellen. Das Verständnis von Sätzen ist die richtige Funktionalität, um unser Problem aus dem vorherigen Unterabschnitt zu lösen. Wir sind in der Lage, die Menge der Nicht-Primzahlen ohne Dubletten zu erstellen:
from math import sqrt
n = 100
sqrt_n = int(sqrt(n))
kein_prim_zahl = {j for i in range(2, sqrt_n+1) for j in range(i*2, n, i)}
kein_prim_zahl
prim_zahlen= {i for i in range(2, n) if i not in kein_prim_zahl}
print(prim_zahlen)
from math import sqrt
def prim_zahlen(n):
if n == 0:
return []
elif n == 1:
return []
else:
p = prim_zahlen(int(sqrt(n)))
no_p = {j for i in p for j in range(i*2, n+1, i)}
p = {x for x in range(2, n + 1) if x not in no_p}
return p
for i in range(1,50):
print(i, prim_zahlen(i))
x = "This value will be changed in the list comprehension" res = [x for x in range(3)] res [0, 1, 2] x 2 res = [i for i in range(5)] i 4
Guido van Rossum bezeichnete diesen Effekt als "eines von Pythons 'schmutzigen kleinen Geheimnissen' seit Jahren" .1 Der Grund dafür war die Effizienz. "Es begann als absichtlicher Kompromiss, Listenverständnisse unglaublich schnell zu machen, und obwohl es für Anfänger keine häufige Gefahr war, stach es definitiv gelegentlich Menschen."
Dieses "schmutzige kleine Geheimnis" ist in Python3 behoben, wie Sie im folgenden Code sehen können:
x = "Python 3 fixed the dirty little secret"
res = [x for x in range(3)]
print(res)
[0, 1, 2]
x
Fußnoten: 1 Guido van Rossum: From List Comprehensions to Generator Expressions
2 dto.