Python Language
Lijstbegrippen
Zoeken…
Invoering
[i ** 2 for i in range(1,11)]
De dummy i
uit een bestaand range
wordt gebruikt om een nieuw elementenpatroon te maken. Het wordt gebruikt waar een for-lus nodig zou zijn in minder expressieve talen.
Syntaxis
- [i voor i binnen bereik (10)] # basislijstbegrip
- [i voor i in xrange (10)] # basislijstbegrip met generatorobject in python 2.x
- [i voor i binnen bereik (20) als i% 2 == 0] # met filter
- [x + y voor x in [1, 2, 3] voor y in [3, 4, 5]] # geneste lussen
- [i als i> 6 anders 0 voor i in bereik (10)] # ternaire uitdrukking
- [i als i> 4 anders 0 voor i binnen bereik (20) als i% 2 == 0] # met filter en ternaire uitdrukking
- [[x + y voor x in [1, 2, 3]] voor y in [3, 4, 5]] # geneste lijstbegrip
Opmerkingen
Lijstbegrippen werden uiteengezet in PEP 202 en geïntroduceerd in Python 2.0.
Voorwaardelijke lijstbegrippen
Gegeven een lijst begrip kunt u een of meer toevoegen if
voorwaarden voor filter waarden.
[<expression> for <element> in <iterable> if <condition>]
Voor elk <element>
in <iterable>
; als <condition>
resulteert in True
, voegt u <expression>
(meestal een functie van <element>
) toe aan de geretourneerde lijst.
Dit kan bijvoorbeeld worden gebruikt om alleen even getallen uit een reeks gehele getallen te extraheren:
[x for x in range(10) if x % 2 == 0]
# Out: [0, 2, 4, 6, 8]
De bovenstaande code is gelijk aan:
even_numbers = []
for x in range(10):
if x % 2 == 0:
even_numbers.append(x)
print(even_numbers)
# Out: [0, 2, 4, 6, 8]
Ook is een voorwaardelijke lijstbegrip van de vorm [e for x in y if c]
(waarbij e
en c
uitdrukkingen zijn in termen van x
) equivalent aan list(filter(lambda x: c, map(lambda x: e, y)))
.
Ondanks hetzelfde resultaat, let op het feit dat het eerste exemplaar bijna 2x sneller is dan het laatste. Voor degenen die nieuwsgierig zijn, is dit een mooie uitleg van de reden waarom.
Merk op dat dit heel anders is dan de ... if ... else ...
voorwaardelijke expressie (ook wel een ternaire expressie genoemd ) die u kunt gebruiken voor het <expression>
gedeelte van het lijstbegrip. Overweeg het volgende voorbeeld:
[x if x % 2 == 0 else None for x in range(10)]
# Out: [0, None, 2, None, 4, None, 6, None, 8, None]
Hier is de voorwaardelijke uitdrukking geen filter, maar eerder een operator die de waarde bepaalt die voor de lijstitems moet worden gebruikt:
<value-if-condition-is-true> if <condition> else <value-if-condition-is-false>
Dit wordt duidelijker als je het combineert met andere operatoren:
[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]
Als u Python 2.7 gebruikt, kan xrange
om verschillende redenen beter zijn dan range
zoals beschreven in de xrange
documentatie .
[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]
De bovenstaande code is gelijk aan:
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]
Men kan ternaire uitdrukkingen en if
voorwaarden combineren. De ternaire operator werkt op het gefilterde resultaat:
[x if x > 2 else '*' for x in range(10) if x % 2 == 0]
# Out: ['*', '*', 4, 6, 8]
Hetzelfde kon niet alleen door de ternaire operator worden bereikt:
[x if (x > 2 and x % 2 == 0) else '*' for x in range(10)]
# Out:['*', '*', '*', '*', 4, '*', 6, '*', 8, '*']
Zie ook: Filters , die vaak een voldoende alternatief bieden voor voorwaardelijke lijstbegrippen.
Lijstbegrippen met geneste lussen
Lijstbegrippen kunnen genest for
lussen gebruiken. U kunt binnen een lijstbegrip een willekeurig aantal geneste for-lussen coderen, en elke for
lus kan een optionele bijbehorende if
test hebben. Wanneer u dit doet, is de volgorde van de for
constructies dezelfde volgorde als wanneer u een reeks geneste for
instructies schrijft. De algemene structuur van lijstbegrippen ziet er als volgt uit:
[ expression for target1 in iterable1 [if condition1]
for target2 in iterable2 [if condition2]...
for targetN in iterableN [if conditionN] ]
Bijvoorbeeld de volgende code die een lijst met lijsten afvlakt met behulp van meerdere for
instructies:
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 equivalent worden geschreven als een lijstbegrip met meerdere for
constructen:
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]
In zowel de uitgebreide vorm als het begrip van de lijst komt de buitenste lus (eerst voor verklaring) eerst.
Naast het compacter zijn, is het geneste begrip ook aanzienlijk sneller.
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
De overhead voor de functieoproep hierboven is ongeveer 140ns .
Inline if
s op dezelfde manier zijn genest en na de eerste in elke positie kunnen voorkomen 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]
Voor de leesbaarheid moet u echter overwegen traditionele for-loops te gebruiken . Dit is met name het geval wanneer nestelen meer dan 2 niveaus diep is en / of de logica van het begrip te complex is. begrip van meerdere geneste luslijsten kan foutgevoelig zijn of geeft onverwacht resultaat.
Filter en kaart opnieuw definiëren om begrippen op te sommen
De filter
of map
functies moeten vaak vervangen worden door Lijstcomprehensies . Guido Van Rossum beschrijft dit goed in een open brief in 2005 :
filter(P, S)
wordt bijna altijd duidelijker geschreven als[x for x in S if P(x)]
, en dit heeft het enorme voordeel dat de meest voorkomende gebruiken predicaten zijn die vergelijkingen zijn, bijvoorbeeldx==42
, en definiërend een lambda daarvoor vereist gewoon veel meer moeite voor de lezer (plus de lambda is langzamer dan het begrip van de lijst). Dit geldt nog meer voor demap(F, S)
die[F(x) for x in S]
. Natuurlijk kun je in veel gevallen generatoruitdrukkingen gebruiken.
De volgende coderegels worden beschouwd als " niet pythonisch " en zullen fouten veroorzaken in veel 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
Het nemen van wat we hebben geleerd van de vorige citaat, kunnen we naar beneden deze breken filter
en map
uitdrukkingen in hun tegenwaarde Lijstcomprehensies; ook het verwijderen van de lambdafuncties van elk - waardoor de code leesbaarder wordt in het proces.
# 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)]
De leesbaarheid wordt nog duidelijker wanneer het gaat om kettingfuncties. Waar vanwege de leesbaarheid de resultaten van de ene map of filterfunctie moeten worden doorgegeven als een resultaat; in eenvoudige gevallen kunnen deze worden vervangen door een enkele lijstbegrip. Verder kunnen we gemakkelijk uit de lijst begrijpen wat de uitkomst van ons proces is, waar er meer cognitieve belasting is bij het redeneren over het geketende Map & Filter-proces.
# 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 - Snelle referentie
Kaart
map(F, S) == [F(x) for x in S]
Filter
filter(P, S) == [x for x in S if P(x)]
waarbij F
en P
functies zijn die respectievelijk invoerwaarden transformeren en een bool
retourneren
Geneste lijstbegrippen
Geneste lijstbegrippen, in tegenstelling tot lijstbegrippen met geneste lussen, zijn Lijstbegrippen binnen een lijstbegrip. De eerste uitdrukking kan elke willekeurige uitdrukking zijn, inclusief een ander begrip van de lijst.
#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]]
Het geneste voorbeeld is gelijk aan
l = []
for y in [3, 4, 5]:
temp = []
for x in [1, 2, 3]:
temp.append(x + y)
l.append(temp)
Een voorbeeld waarbij een genest begrip kan worden gebruikt om een matrix om te zetten.
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]]
Zoals genest for
lussen, is er geen limiet aan hoe diep begrip kan worden genest.
[[[i + j + k for k in 'cd'] for j in 'ab'] for i in '12']
# Out: [[['1ac', '1ad'], ['1bc', '1bd']], [['2ac', '2ad'], ['2bc', '2bd']]]
Herhaal twee of meer lijsten tegelijkertijd binnen lijstbegrip
Voor het gelijktijdig herhalen van meer dan twee lijsten binnen het begrip van de lijst , kan men zip()
als:
>>> 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 ...