Zoeken…


Invoering

Een lijstbegrip is een syntactisch hulpmiddel voor het maken van lijsten op een natuurlijke en beknopte manier, zoals geïllustreerd in de volgende code om een lijst te maken van vierkanten van de getallen 1 tot 10: [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]

Live demonstratie

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]

Live demonstratie

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]

Live demonstratie

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]

Live demonstratie

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]

Live demonstratie

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, bijvoorbeeld x==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 de map(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 ...


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow