Python Language
Zrozumienie listy
Szukaj…
Wprowadzenie
[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]
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]
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]
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]
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]
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 dlamap(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 ...