Szukaj…


Wprowadzenie

Zrozumienie listy jest składniowym narzędziem do tworzenia list w naturalny i zwięzły sposób, jak pokazano w poniższym kodzie, aby utworzyć listę kwadratów o numerach od 1 do 10: [i ** 2 for i in range(1,11)] Manekin i z istniejącego range listy służy do utworzenia nowego wzorca elementu. Jest używany, gdy pętla for byłaby konieczna w mniej ekspresyjnych językach.

Składnia

  • [i dla i w zakresie (10)] # podstawowe zrozumienie listy
  • [i for i w xrange (10)] # podstawowe rozumienie listy z obiektem generatora w python 2.x
  • [i dla i w zakresie (20), jeśli i% 2 == 0] # z filtrem
  • [x + y dla x w [1, 2, 3] dla y w [3, 4, 5]] # zagnieżdżonych pętli
  • [i if i> 6 else 0 dla i w zakresie (10)] # wyrażenie trójskładnikowe
  • [i if i> 4 else 0 dla i w zakresie (20) if i% 2 == 0] # z filtrem i wyrażeniem trójskładnikowym
  • [[x + y dla x w [1, 2, 3]] dla y w [3, 4, 5]] # zrozumienie listy zagnieżdżonej

Uwagi

Wyjaśnienia list zostały przedstawione w PEP 202 i wprowadzone w Pythonie 2.0.

Zrozumienie listy warunkowej

Biorąc pod uwagę listę , możesz dołączyć jeden lub więcej, if warunki do filtrowania wartości.

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

Dla każdego <element> w <iterable> ; jeśli <condition> wartość True , dodaj <expression> (zwykle funkcja <element> ) do zwróconej listy.


Na przykład można tego użyć do wyodrębnienia tylko liczb parzystych z sekwencji liczb całkowitych:

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

Demo na żywo

Powyższy kod jest równoważny z:

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

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

Również warunkowe rozumienie listy w postaci [e for x in y if c] (gdzie e i c są wyrażeniami w kategoriach x ) jest równoważne list(filter(lambda x: c, map(lambda x: e, y))) .

Pomimo tego samego rezultatu, zwróć uwagę na fakt, że pierwszy przykład jest prawie 2x szybszy od drugiego. Dla tych, którzy są ciekawi, to jest ładne wyjaśnienie przyczyny.


Zauważ, że jest to zupełnie inne niż ... if ... else ... wyrażenie warunkowe (czasami nazywane wyrażeniem potrójnym ), którego możesz użyć do części <expression> listy <expression> . Rozważ następujący przykład:

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

Demo na żywo

W tym przypadku wyrażenie warunkowe nie jest filtrem, lecz operatorem określającym wartość, która ma być użyta dla elementów listy:

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

Staje się to bardziej oczywiste, jeśli połączysz go z innymi operatorami:

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

Demo na żywo

Jeśli używasz Python 2.7, xrange może być lepszy niż range z kilku powodów opisanych w dokumentacji xrange .

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

Powyższy kod jest równoważny z:

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]

Można połączyć trójskładnikowych wyrażeń i if warunki. Operator trójskładnikowy działa na odfiltrowanym wyniku:

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

Tego samego nie można było osiągnąć tylko przez operatora trójskładnikowego:

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

Zobacz także: Filtry , które często stanowią wystarczającą alternatywę dla list warunkowych.

Wyjaśnij listę za pomocą zagnieżdżonych pętli

Listowych można używać zagnieżdżone for pętli. Można zakodować dowolną liczbę zagnieżdżone dla pętli wewnątrz wyrażeń listowych, a każda for pętli może mieć opcjonalny związane if testu. Czyniąc to, Zakon for konstrukcji jest taka sama kolejność jak przy pisaniu serię zagnieżdżonych for sprawozdania. Ogólna struktura zestawień list wygląda następująco:

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

Na przykład, następujący kod spłaszczenie listę list Korzystanie z wielu for stwierdzeń:

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]

może być napisany w sposób równoważny jako lista z wielokrotnością for konstrukcji:

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]

Demo na żywo

Zarówno w formie rozszerzonej, jak i w postaci listy, zewnętrzna pętla (pierwsza dla instrukcji) jest na pierwszym miejscu.


Oprócz tego, że jest bardziej kompaktowy, zrozumienie zagnieżdżone jest również znacznie szybsze.

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

Narzut związany z powyższym wywołaniem funkcji wynosi około 140ns .


Wstaw, if s są zagnieżdżone podobnie i mogą wystąpić w dowolnej pozycji po pierwszym 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]

Demo na żywo

Jednak ze względu na czytelność powinieneś rozważyć użycie tradycyjnych pętli for . Jest to szczególnie prawdziwe, gdy zagnieżdżanie ma więcej niż 2 poziomy głębokości i / lub logika zrozumienia jest zbyt złożona. zrozumienie wielu zagnieżdżonych list pętli może być podatne na błędy lub daje nieoczekiwany wynik.

Refaktoryzacja filtru i mapy do listy wyrażeń

Funkcje filter lub map należy często zastępować zrozumieniem listy . Guido Van Rossum opisuje to dobrze w liście otwartym w 2005 r . :

filter(P, S) jest prawie zawsze zapisany jaśniej jako [x for x in S if P(x)] , i ma to tę ogromną zaletę, że najczęściej stosowane zastosowania obejmują predykaty, które są porównaniami, np. x==42 , i definiowanie lambda do tego wymaga po prostu dużo więcej wysiłku dla czytelnika (a ponadto lambda jest wolniejsza niż zrozumienie listy). Tym bardziej dla map(F, S) która staje się [F(x) for x in S] . Oczywiście w wielu przypadkach można zamiast tego użyć wyrażeń generatora.

Poniższe wiersze kodu są uważane za „ nie Pythonic ” i powodują błędy w wielu linterach pytona.

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

Biorąc to, czego nauczyliśmy się z poprzedniego cytatu, możemy rozbić te wyrażenia filter i map na ich odpowiedniki z list ; usuwając również funkcje lambda z każdego z nich - dzięki temu kod jest bardziej czytelny w tym procesie.

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

Czytelność staje się jeszcze bardziej widoczna w przypadku funkcji łańcuchowych. Tam, gdzie ze względu na czytelność wyniki jednej mapy lub funkcji filtra powinny być przekazywane w następnej kolejności; w prostych przypadkach można je zastąpić pojedynczą listą. Co więcej, na podstawie zrozumienia listy możemy z łatwością stwierdzić, jaki jest wynik naszego procesu, gdzie jest więcej obciążenia poznawczego podczas wnioskowania o połączonym procesie Map & Filter.

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

Refaktoryzacja - Krótki przegląd

  • Mapa

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

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

gdzie F i P są funkcjami, które odpowiednio przekształcają wartości wejściowe i zwracają wartość bool

Zrozumienia listy zagnieżdżonej

Rozumienia list zagnieżdżonych, w odróżnieniu od wyliczeń list z pętlami zagnieżdżonymi, to Rozumienia list w obrębie rozumienia listy. Pierwszym wyrażeniem może być dowolne wyrażenie, w tym inne rozumienie listy.

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

Przykład zagnieżdżony jest równoważny z

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

Jeden przykład, w którym zagnieżdżone rozumienie można wykorzystać do transponowania macierzy.

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

Podobnie jak zagnieżdżone for pętli, nie ma ograniczeń co do tego, jak głębokie zrozumienia mogą być zagnieżdżone.

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

Iteruj dwie lub więcej list jednocześnie w ramach ich rozumienia

Do iteracji więcej niż dwóch list jednocześnie w ramach ich interpretacji można użyć zip() jako:

>>> 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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow