Suche…


Einführung

Ein Listenverständnis ist ein syntaktisches Werkzeug zum Erstellen von Listen auf natürliche und prägnante Weise, wie im folgenden Code veranschaulicht, um eine Liste mit Quadraten der Zahlen 1 bis 10 zu erstellen: [i ** 2 for i in range(1,11)] Die Dummy - i aus einer bestehenden Liste range ist ein neues Element Muster machen verwendet. Es wird verwendet, wenn eine for-Schleife in weniger ausdrucksstarken Sprachen erforderlich ist.

Syntax

  • [i für i in range (10)] # grundlegendes Listenverständnis
  • [i for i in xrange (10)] # grundlegendes Listenverständnis mit Generatorobjekt in Python 2.x
  • [i für i im Bereich (20), wenn i% 2 == 0] # mit Filter
  • [x + y für x in [1, 2, 3] für y in [3, 4, 5]] # verschachtelten Schleifen
  • [i wenn i> 6, sonst 0 für i im Bereich (10)] # ternärer Ausdruck
  • [i wenn i> 4 sonst 0 für i im Bereich (20), wenn i% 2 == 0] # mit Filter und ternärem Ausdruck
  • [[x + y für x in [1, 2, 3]] für y in [3, 4, 5]] # geschachteltes Listenverständnis

Bemerkungen

Listenverständnisse wurden in PEP 202 beschrieben und in Python 2.0 eingeführt.

Bedingte Listenverständnisse

Bei einem Listenverständnis können Sie eine oder mehrere if Bedingungen anhängen, um Werte zu filtern.

[<expression> for <element> in <iterable> if <condition>]

Für jedes <element> in <iterable> ; Wenn <condition> zu True ausgewertet wird, fügen Sie der zurückgegebenen Liste <expression> (normalerweise eine Funktion von <element> ) hinzu.


Dies kann zum Beispiel verwendet werden, um nur gerade Zahlen aus einer Folge von ganzen Zahlen zu extrahieren:

[x for x in range(10) if x % 2 == 0]
# Out: [0, 2, 4, 6, 8]

Live-Demo

Der obige Code entspricht:

even_numbers = [] 
for x in range(10):
    if x % 2 == 0:
        even_numbers.append(x)

print(even_numbers)
# Out: [0, 2, 4, 6, 8]

Auch ein bedingtes Listenverständnis der Form [e for x in y if c] (wobei e und c Ausdrücke in Form von x ) ist gleichbedeutend mit list(filter(lambda x: c, map(lambda x: e, y))) .

Beachten Sie trotz des gleichen Ergebnisses, dass das erste Beispiel fast doppelt so schnell ist wie das zweite. Für Neugierige ist dies eine nette Erklärung für den Grund.


Beachten Sie, dass dies ganz anders ist als der Bedingungsausdruck ... if ... else ... (manchmal auch als ternärer Ausdruck bezeichnet ), den Sie für den Teil <expression> des Listenverständnisses verwenden können. Betrachten Sie das folgende Beispiel:

[x if x % 2 == 0 else None for x in range(10)]
# Out: [0, None, 2, None, 4, None, 6, None, 8, None]

Live-Demo

Hier ist der Bedingungsausdruck kein Filter, sondern ein Operator, der den für die Listenelemente zu verwendenden Wert bestimmt:

<value-if-condition-is-true> if <condition> else <value-if-condition-is-false>

Dies wird deutlicher, wenn Sie es mit anderen Operatoren kombinieren:

[2 * (x if x % 2 == 0 else -1) + 1 for x in range(10)]
# Out: [1, -1, 5, -1, 9, -1, 13, -1, 17, -1]

Live-Demo

Wenn Sie Python 2.7 verwenden, ist xrange aus verschiedenen Gründen möglicherweise besser als der range wie in der xrange Dokumentation beschrieben .

[2 * (x if x % 2 == 0 else -1) + 1 for x in xrange(10)]
# Out: [1, -1, 5, -1, 9, -1, 13, -1, 17, -1]

Der obige Code entspricht:

numbers = []
for x in range(10):
    if x % 2 == 0:
        temp = x
    else:
        temp = -1
    numbers.append(2 * temp + 1)
print(numbers)
# Out: [1, -1, 5, -1, 9, -1, 13, -1, 17, -1]

Man kann ternäre Ausdrücke und if Bedingungen kombinieren. Der ternäre Operator arbeitet mit dem gefilterten Ergebnis:

[x if x > 2 else '*' for x in range(10) if x % 2 == 0]
# Out: ['*', '*', 4, 6, 8]

Dasselbe konnte nicht nur von einem ternären Operator erreicht werden:

[x if (x > 2 and x % 2 == 0) else '*' for x in range(10)]
# Out:['*', '*', '*', '*', 4, '*', 6, '*', 8, '*']

Siehe auch: Filter , die häufig eine ausreichende Alternative zu bedingten Listenverständnissen darstellen.

Listenverständnisse mit verschachtelten Schleifen

Listenverständnisse können verschachtelt for Schleifen verwendet werden. Sie können eine beliebige Anzahl von verschachtelten for-Schleifen innerhalb eines Listenverständnisses codieren. Jeder for Schleife kann ein optionaler if Test zugeordnet werden. Dabei ist die Reihenfolge der for ist Konstrukte die gleiche Reihenfolge wie bei einer Reihe von verschachtelten Schreiben for Anweisungen. Die allgemeine Struktur von Listenverstehen sieht folgendermaßen aus:

[ expression for target1 in iterable1 [if condition1]
             for target2 in iterable2 [if condition2]...
             for targetN in iterableN [if conditionN] ]

Der folgende Code reduziert beispielsweise eine Liste mit mehreren for Anweisungen:

data = [[1, 2], [3, 4], [5, 6]]
output = []
for each_list in data:
    for element in each_list:
        output.append(element)
print(output)
# Out: [1, 2, 3, 4, 5, 6]

kann als Listenverständnis mit mehreren for Konstrukte äquivalent geschrieben werden:

data = [[1, 2], [3, 4], [5, 6]]
output = [element for each_list in data for element in each_list]
print(output)
# Out: [1, 2, 3, 4, 5, 6]

Live Demo

Sowohl in der erweiterten Form als auch im Listenverständnis steht die äußere Schleife (erste Anweisung) an erster Stelle.


Das verschachtelte Verständnis ist nicht nur kompakter, sondern auch wesentlich schneller.

In [1]: data = [[1,2],[3,4],[5,6]]
In [2]: def f():
   ...:     output=[]
   ...:     for each_list in data:
   ...:         for element in each_list:
   ...:             output.append(element)
   ...:     return output
In [3]: timeit f()
1000000 loops, best of 3: 1.37 µs per loop
In [4]: timeit [inner for outer in data for inner in outer]
1000000 loops, best of 3: 632 ns per loop

Der Aufwand für den Funktionsaufruf oben beträgt ungefähr 140ns .


Inline, if s ähnlich verschachtelt sind und an einer beliebigen Stelle nach der ersten for :

data = [[1], [2, 3], [4, 5]]
output = [element for each_list in data
                if len(each_list) == 2
                for element in each_list
                if element != 5]
print(output)
# Out: [2, 3, 4]

Live Demo

Der besseren Lesbarkeit halber sollten Sie jedoch traditionelle For-Loops verwenden . Dies gilt insbesondere, wenn das Schachteln mehr als zwei Ebenen tief ist und / oder die Logik des Verständnisses zu komplex ist. Das Verständnis für mehrere verschachtelte Schleifenlisten kann fehleranfällig sein oder unerwartete Ergebnisse liefern.

Refactoring-Filter und Karte zum Auflisten von Verständnis

Die filter oder map sollten häufig durch Listenverständnisse ersetzt werden. Guido Van Rossum beschreibt dies gut in einem offenen Brief aus dem Jahr 2005 :

filter(P, S) wird fast immer klarer geschrieben als [x for x in S if P(x)] , und dies hat den großen Vorteil, dass die häufigsten Verwendungen Vergleichselemente beinhalten, z. B. x==42 und definierend Ein Lambda dafür erfordert nur viel mehr Aufwand für den Leser (plus das Lambda ist langsamer als das Listenverständnis). Dies gilt umso mehr für die map(F, S) die zu [F(x) for x in S] . In vielen Fällen können Sie jedoch stattdessen Generatorausdrücke verwenden.

Die folgenden Codezeilen gelten als " nicht pythonisch " und verursachen Fehler in vielen Python-Linters.

filter(lambda x: x % 2 == 0, range(10)) # even numbers < 10
map(lambda x: 2*x, range(10)) # multiply each number by two
reduce(lambda x,y: x+y, range(10)) # sum of all elements in list

Nach dem, was wir aus dem vorherigen Zitat gelernt haben, können wir diese filter und map in ihre entsprechenden Listenverständnisse unterteilen . Entfernen Sie auch die Lambda- Funktionen von jedem - dadurch wird der Code lesbarer.

# Filter:
# P(x) = x % 2 == 0
# S = range(10)
[x for x in range(10) if x % 2 == 0]

# Map
# F(x) = 2*x
# S = range(10)
[2*x for x in range(10)]

Die Lesbarkeit wird noch deutlicher bei Verkettungsfunktionen. Wo aus Gründen der Lesbarkeit die Ergebnisse einer Map- oder Filterfunktion an die nächste weitergegeben werden sollten; In einfachen Fällen können diese durch ein einzelnes Listenverständnis ersetzt werden. Darüber hinaus können wir anhand des Listenverständnisses leicht erkennen, was das Ergebnis unseres Prozesses ist, wo die kognitive Belastung höher ist, wenn über den verketteten Map & Filter-Prozess vorgegangen wird.

# Map & Filter
filtered = filter(lambda x: x % 2 == 0, range(10))
results = map(lambda x: 2*x, filtered)

# List comprehension
results = [2*x for x in range(10) if x % 2 == 0]

Refactoring - Kurzanleitung

  • Karte

    map(F, S) == [F(x) for x in S]
    
  • Filter

    filter(P, S) == [x for x in S if P(x)]
    

Dabei sind F und P Funktionen, die jeweils Eingangswerte transformieren und einen bool

Verschachtelte Listenverständnisse

Geschachtelte Listenverständnisse sind im Gegensatz zu Listenverständnissen mit verschachtelten Schleifen Listenverständnisse im Listenverständnis. Der Anfangsausdruck kann ein beliebiger Ausdruck sein, einschließlich eines anderen Listenverständnisses.

#List Comprehension with nested loop
[x + y for x in [1, 2, 3] for y in [3, 4, 5]]
#Out: [4, 5, 6, 5, 6, 7, 6, 7, 8]

#Nested List Comprehension
[[x + y for x in [1, 2, 3]] for y in [3, 4, 5]]
#Out: [[4, 5, 6], [5, 6, 7], [6, 7, 8]]

Das verschachtelte Beispiel ist äquivalent zu

l = []
for y in [3, 4, 5]:
    temp = []
    for x in [1, 2, 3]:
        temp.append(x + y)
    l.append(temp)

Ein Beispiel, bei dem ein verschachteltes Verständnis verwendet werden kann, um eine Matrix zu transponieren.

matrix = [[1,2,3],
          [4,5,6],
          [7,8,9]] 

[[row[i] for row in matrix] for i in range(len(matrix))]
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Wie bei verschachtelten for Schleifen gibt es keine Grenzen dafür, wie tiefes Verständnis verschachtelt werden kann.

[[[i + j + k for k in 'cd'] for j in 'ab'] for i in '12']
# Out: [[['1ac', '1ad'], ['1bc', '1bd']], [['2ac', '2ad'], ['2bc', '2bd']]]

Iteriere zwei oder mehr Listen gleichzeitig innerhalb des Listenverständnisses

Um innerhalb des Listenverständnisses mehr als zwei Listen gleichzeitig zu durchlaufen , kann zip() werden:

>>> list_1 = [1, 2, 3 , 4]
>>> list_2 = ['a', 'b', 'c', 'd']
>>> list_3 = ['6', '7', '8', '9']

# Two lists
>>> [(i, j) for i, j in zip(list_1, list_2)]
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]

# Three lists
>>> [(i, j, k) for i, j, k in zip(list_1, list_2, list_3)]
[(1, 'a', '6'), (2, 'b', '7'), (3, 'c', '8'), (4, 'd', '9')]

# so on ...


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow