Python Language
Lista förståelser
Sök…
Introduktion
[i ** 2 for i in range(1,11)]
Dockan i
från en befintlig lista range
används för att göra ett nytt element mönster. Det används där en for loop skulle vara nödvändig på mindre uttrycksfulla språk.
Syntax
- [i för i inom intervallet (10)] # grundläggande listaförståelse
- [i för i i xrange (10)] # grundläggande listaförståelse med generatorobjekt i python 2.x
- [i för i inom intervallet (20) om jag% 2 == 0] # med filter
- [x + y för x i [1, 2, 3] för y i [3, 4, 5]] # kapslade öglor
- [i om i> 6 annars 0 för i inom intervallet (10)] # ternärt uttryck
- [i om i> 4 annars 0 för i inom intervallet (20) om i% 2 == 0] # med filter och ternärt uttryck
- [[x + y för x i [1, 2, 3]] för y i [3, 4, 5]] # kapslad listförståelse
Anmärkningar
Listförståelser beskrivs i PEP 202 och introducerades i Python 2.0.
Villkorliga lista Förståelser
Med en listaförståelse kan du lägga till en eller flera if
villkor för att filtrera värden.
[<expression> for <element> in <iterable> if <condition>]
För varje <element>
i <iterable>
; om <condition>
utvärderas till True
, lägg till <expression>
(vanligtvis en funktion av <element>
) till den returnerade listan.
Till exempel kan detta användas för att extrahera endast jämna nummer från en helhetssekvens:
[x for x in range(10) if x % 2 == 0]
# Out: [0, 2, 4, 6, 8]
Ovanstående kod motsvarar:
even_numbers = []
for x in range(10):
if x % 2 == 0:
even_numbers.append(x)
print(even_numbers)
# Out: [0, 2, 4, 6, 8]
Dessutom är en villkorad listförståelse av formen [e for x in y if c]
(där e
och c
är uttryck i termer av x
) ekvivalent med list(filter(lambda x: c, map(lambda x: e, y)))
.
Trots att du ger samma resultat, var uppmärksam på att det förra exemplet är nästan 2x snabbare än det senare. För dig som är nyfiken är detta en trevlig förklaring av orsaken till det.
Observera att detta skiljer sig helt från ... if ... else ...
villkorat uttryck (ibland känt som ett ternärt uttryck ) som du kan använda för <expression>
-delen i listförståelsen. Tänk på följande exempel:
[x if x % 2 == 0 else None for x in range(10)]
# Out: [0, None, 2, None, 4, None, 6, None, 8, None]
Här är det villkorliga uttrycket inte ett filter, utan en operatör som bestämmer värdet som ska användas för listobjekten:
<value-if-condition-is-true> if <condition> else <value-if-condition-is-false>
Detta blir mer uppenbart om du kombinerar det med andra operatörer:
[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]
Om du använder Python 2.7, kan xrange
vara bättre än range
av flera skäl som beskrivs i dokumentationen för 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]
Ovanstående kod motsvarar:
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 kan kombinera ternära uttryck och if
villkor. Den ternära operatören fungerar på det filtrerade resultatet:
[x if x > 2 else '*' for x in range(10) if x % 2 == 0]
# Out: ['*', '*', 4, 6, 8]
Detsamma kunde inte ha uppnåtts bara av en ternär operatör:
[x if (x > 2 and x % 2 == 0) else '*' for x in range(10)]
# Out:['*', '*', '*', '*', 4, '*', 6, '*', 8, '*']
Se även: Filter , som ofta ger ett tillräckligt alternativ till villkorade listförståelser.
Lista förståelser med kapslade slingor
Listförståelser kan använda kapslade for
slingor. Du kan koda valfritt antal kapslade för slingor inom en listaförståelse, och varje for
loop kan ha ett valfritt tillhörande if
test. När du gör det är ordningen på for
konstruktioner samma ordning som när du skriver en serie kapslade for
uttalanden. Den allmänna strukturen för listförståelser ser så här ut:
[ expression for target1 in iterable1 [if condition1]
for target2 in iterable2 [if condition2]...
for targetN in iterableN [if conditionN] ]
Till exempel, följande kod plattar ut en lista med listor med flera for
uttalanden:
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]
kan skrivas på motsvarande sätt som en listaförståelse med multipel for
konstruktioner:
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]
I både den utvidgade formen och listförståelsen kommer den yttre slingan (först för uttalande) först.
Förutom att vara mer kompakt är den kapslade förståelsen också betydligt snabbare.
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
Kostnaden för funktionssamtalet ovan är cirka 140n .
Inline if
s är kapslade på liknande sätt och kan förekomma i valfri position efter den första 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]
För läsbarhetens skull bör du dock överväga att använda traditionella för-slingor . Detta är särskilt sant när häckningen är mer än 2 nivåer djup och / eller logiken för förståelsen är för komplex. flera kapslade slingor i listan kan vara felaktiga eller det ger oväntat resultat.
Refactoring filter och karta för att lista förståelser
De filter
eller map
bör ofta bytas ut mot list uppfattningar . Guido Van Rossum beskriver detta väl i ett öppet brev 2005 :
filter(P, S)
skrivs nästan alltid tydligare som[x for x in S if P(x)]
, och detta har den enorma fördelen att de vanligaste användningarna involverar predikat som är jämförelser, t.ex.x==42
, och definiera en lambda för det kräver bara mycket mer ansträngning för läsaren (plus att lambda är långsammare än listförståelsen). Ännu mer förmap(F, S)
som blir[F(x) for x in S]
. Naturligtvis skulle du i många fall kunna använda generatoruttryck istället.
Följande kodrader betraktas som " inte pythoniska " och kommer att ge fel i många pythonlinters.
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
Med det vi har lärt oss från föregående citat kan vi dela upp dessa filter
och map
uttryck i deras motsvarande listförståelser ; ta också bort lambda- funktionerna från var och en - vilket gör koden mer läsbar i processen.
# 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)]
Läsbarheten blir ännu tydligare när man hanterar kedjefunktioner. Där resultaten av en karta eller filterfunktion på grund av läsbarheten ska skickas som ett resultat till nästa; med enkla fall kan dessa ersättas med en enda listförståelse. Vidare kan vi från listförståelsen enkelt berätta vad resultatet av vår process är, där det finns mer kognitiv belastning när vi resonerar om den kedjade Map & Filter-processen.
# 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 - snabbreferens
Karta
map(F, S) == [F(x) for x in S]
Filtrera
filter(P, S) == [x for x in S if P(x)]
där F
och P
är funktioner som transformerar respektive ingångsvärden och returnerar en bool
Kapslade listförståelser
Kapslade listförståelser, till skillnad från listförståelser med kapslade slingor, är listförståelser inom en listförståelse. Det första uttrycket kan vara vilket godtyckligt uttryck som helst, inklusive en annan listaförståelse.
#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]]
Exempel-exemplet motsvarar
l = []
for y in [3, 4, 5]:
temp = []
for x in [1, 2, 3]:
temp.append(x + y)
l.append(temp)
Ett exempel där en kapslad förståelse kan användas för att transponera en matris.
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]]
Precis som kapslade for
slingor finns det ingen gräns för hur djupa förståelser kan häckas.
[[[i + j + k for k in 'cd'] for j in 'ab'] for i in '12']
# Out: [[['1ac', '1ad'], ['1bc', '1bd']], [['2ac', '2ad'], ['2bc', '2bd']]]
Iterera två eller flera listor samtidigt inom listförståelse
För att upprepa mer än två listor samtidigt inom listförståelse kan man använda zip()
som:
>>> 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 ...