Zoeken…


Invoering

Lijstbegrippen in Python zijn beknopte, syntactische constructies. Ze kunnen worden gebruikt om lijsten van andere lijsten te genereren door functies toe te passen op elk element in de lijst. In de volgende sectie wordt het gebruik van deze uitdrukkingen uitgelegd en gedemonstreerd.

Syntaxis

  • [x + 1 voor x in (1, 2, 3)] # lijstbegrip, geeft [2, 3, 4]
  • (x + 1 voor x in (1, 2, 3)) # generatoruitdrukking, levert 2, vervolgens 3 en vervolgens 4 op
  • [x voor x in (1, 2, 3) als x% 2 == 0] # lijstbegrip met filter, geeft [2]
  • [x + 1 als x% 2 == 0 anders x voor x in (1, 2, 3)] # lijstbegrip met ternaire
  • [x + 1 als x% 2 == 0 anders x voor x binnen bereik (-3,4) als x> 0] # lijstbegrip met ternaire en filtering
  • {x voor x in (1, 2, 2, 3)} # begrip, geeft {1, 2, 3}
  • {k: v voor k, v in [('a', 1), ('b', 2)]} # dict begrijpt, geeft {'a': 1, 'b': 2} (python 2.7+ en Alleen 3.0+)
  • [x + y voor x in [1, 2] voor y in [10, 20]] # Geneste lussen, geeft [11, 21, 12, 22]
  • [x + y voor x in [1, 2, 3] als x> 2 voor y in [3, 4, 5]] # Voorwaarde gecontroleerd op 1e voor lus
  • [x + y voor x in [1, 2, 3] voor y in [3, 4, 5] als x> 2] # Voorwaarde gecontroleerd op 2e voor lus
  • [x voor x in xrange (10) als x% 2 == 0] # Voorwaarde aangevinkt als lusnummers oneven nummers zijn

Opmerkingen

Begrippen zijn syntactische constructies die datastructuren of uitdrukkingen definiëren die uniek zijn voor een bepaalde taal. Correct gebruik van begrippen herinterpreteren deze in gemakkelijk te begrijpen uitdrukkingen. Als uitdrukkingen kunnen ze worden gebruikt:

  • aan de rechterkant van opdrachten
  • als argumenten om functies aan te roepen
  • in het lichaam van een lambda-functie
  • als zelfstandige verklaringen. (Bijvoorbeeld: [print(x) for x in range(10)] )

Lijstbegrippen

Een lijstcomprehensie een nieuwe list door toepassing van een expressie aan elk element van een Iterable . De meest basale vorm is:

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

Er is ook een optionele 'als'-voorwaarde:

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

Elk <element> in de <iterable> is aangesloten op de <expression> als de (optionele) <condition> als waar evalueert . Alle resultaten worden in één keer teruggegeven in de nieuwe lijst. Generator-uitdrukkingen worden lui geëvalueerd, maar lijstbegrippen evalueren onmiddellijk de gehele iterator - geheugenverbruik evenredig met de lengte van de iterator.

Een list met vierkante gehele getallen maken:

squares = [x * x for x in (1, 2, 3, 4)]
# squares: [1, 4, 9, 16]

De uitdrukking for stelt x op elke waarde op zijn beurt van (1, 2, 3, 4) . Het resultaat van de uitdrukking x * x wordt toegevoegd aan een interne list . De interne list wordt aan de variabele squares toegewezen wanneer deze is voltooid.

Naast een snelheidsverhoging (zoals hier uitgelegd), is een lijstbegrip ongeveer hetzelfde als de volgende for-lus:

squares = []
for x in (1, 2, 3, 4):
    squares.append(x * x)
# squares: [1, 4, 9, 16]

De uitdrukking die op elk element wordt toegepast, kan zo complex zijn als nodig:

# Get a list of uppercase characters from a string
[s.upper() for s in "Hello World"]
# ['H', 'E', 'L', 'L', 'O', ' ', 'W', 'O', 'R', 'L', 'D']

# Strip off any commas from the end of strings in a list
[w.strip(',') for w in ['these,', 'words,,', 'mostly', 'have,commas,']]
# ['these', 'words', 'mostly', 'have,commas']

# Organize letters in words more reasonably - in an alphabetical order
sentence = "Beautiful is better than ugly"
["".join(sorted(word, key = lambda x: x.lower())) for word in sentence.split()]
# ['aBefiltuu', 'is', 'beertt', 'ahnt', 'gluy']

anders

else kan worden gebruikt in lijstbegripconstructies, maar wees voorzichtig met de syntaxis. De if / else clausules moeten worden gebruikt alvorens for lus, niet na:

# create a list of characters in apple, replacing non vowels with '*'
# Ex - 'apple' --> ['a', '*', '*', '*' ,'e']

[x for x in 'apple' if x in 'aeiou' else '*']
#SyntaxError: invalid syntax

# When using if/else together use them before the loop
[x if x in 'aeiou' else '*' for x in 'apple']
#['a', '*', '*', '*', 'e']

Merk op dat dit een andere taalconstructie gebruikt, een voorwaardelijke uitdrukking , die zelf geen deel uitmaakt van de syntaxis van het begrip . Overwegende dat het if na de for…in een deel van de lijst comprehensies en gebruikt om filterelementen van de bron iterable.


Dubbele herhaling

De volgorde van dubbele iteratie [... for x in ... for y in ...] is natuurlijk of contra-intuïtief. De vuistregel is om een equivalent for lus te volgen:

def foo(i):
    return i, i + 0.5

for i in range(3):
    for x in foo(i):
        yield str(x)

Dit wordt:

[str(x)
    for i in range(3)
        for x in foo(i)
]

Dit kan in één regel worden gecomprimeerd als [str(x) for i in range(3) for x in foo(i)]


In-place mutatie en andere bijwerkingen

Voor het gebruik van lijstcomprehensie, begrijpen het verschil tussen functies opgeroepen voor hun bijwerkingen (muteren, of in-place functies) die meestal terug None , en functies die een interessante waarde te retourneren.

Veel functies (vooral pure functies) nemen eenvoudigweg een object en retourneren een of ander object. Een interne functie verandert het bestaande object, waarbij een bijwerking wordt genoemd. Andere voorbeelden zijn invoer- en uitvoerbewerkingen zoals afdrukken.

list.sort() sorteert een lijst op zijn plaats (wat betekent dat de oorspronkelijke lijst wordt list.sort() ) en retourneert de waarde None . Daarom zal het niet werken zoals verwacht in een lijstbegrip:

[x.sort() for x in [[2, 1], [4, 3], [0, 1]]]
# [None, None, None]

Sort sorted() retourneert in plaats daarvan een gesorteerde list plaats van ter plaatse te sorteren:

[sorted(x) for x in [[2, 1], [4, 3], [0, 1]]]
# [[1, 2], [3, 4], [0, 1]]

Gebruik van begrip voor bijwerkingen is mogelijk, zoals I / O of interne functies. Toch is een for-lus meestal beter leesbaar. Terwijl dit werkt in Python 3:

[print(x) for x in (1, 2, 3)]

Gebruik in plaats daarvan:

for x in (1, 2, 3):
    print(x)

In sommige situaties, bijwerking functies zijn geschikt voor lijstcomprehensie. random.randrange() heeft het neveneffect dat de status van de generator voor willekeurige getallen wordt gewijzigd, maar retourneert ook een interessante waarde. Bovendien kan next() worden aangeroepen op een iterator.

De volgende generator van willekeurige waarden is niet zuiver, maar is wel logisch omdat de willekeurige generator wordt gereset telkens wanneer de uitdrukking wordt geëvalueerd:

from random import randrange
[randrange(1, 7) for _ in range(10)]
# [2, 3, 2, 1, 1, 5, 2, 4, 3, 5]

Witruimte in lijstbegrippen

Meer gecompliceerde lijstbegrippen kunnen een ongewenste lengte bereiken of minder leesbaar worden. Hoewel minder gebruikelijk in voorbeelden, is het mogelijk om een lijstbegrip op te splitsen in meerdere regels zoals:

[
    x for x
    in 'foo'
    if x not in 'bar'
]

Woordenboekbegrippen

Een woordenboekbegrip is vergelijkbaar met een lijstbegrip, behalve dat het een woordenboekobject produceert in plaats van een lijst.

Een basisvoorbeeld:

Python 2.x 2.7
{x: x * x for x in (1, 2, 3, 4)}
# Out: {1: 1, 2: 4, 3: 9, 4: 16}

wat gewoon een andere manier is om te schrijven:

dict((x, x * x) for x in (1, 2, 3, 4))
# Out: {1: 1, 2: 4, 3: 9, 4: 16}

Net als bij het begrip van een lijst, kunnen we een voorwaardelijke verklaring in het dictbegrip gebruiken om alleen de dict-elementen te produceren die aan een criterium voldoen.

Python 2.x 2.7
{name: len(name) for name in ('Stack', 'Overflow', 'Exchange') if len(name) > 6}  
# Out: {'Exchange': 8, 'Overflow': 8}

Of herschreven met behulp van een generatoruitdrukking.

dict((name, len(name)) for name in ('Stack', 'Overflow', 'Exchange') if len(name) > 6)
# Out: {'Exchange': 8, 'Overflow': 8}

Beginnen met een woordenboek en woordenboekbegrip gebruiken als sleutel / waarde-paarfilter

Python 2.x 2.7
initial_dict = {'x': 1, 'y': 2}
{key: value for key, value in initial_dict.items() if key == 'x'}
# Out: {'x': 1}

Schakelsleutel en waarde van woordenboek (woordenboek omkeren)

Als u een dictaat met eenvoudige hash- waarden hebt (dubbele waarden kunnen onverwachte resultaten hebben):

my_dict = {1: 'a', 2: 'b', 3: 'c'}

en je wilde de sleutels en waarden omwisselen, je kunt verschillende benaderingen kiezen, afhankelijk van je codeerstijl:

  • swapped = {v: k for k, v in my_dict.items()}
  • swapped = dict((v, k) for k, v in my_dict.iteritems())
  • swapped = dict(zip(my_dict.values(), my_dict))
  • swapped = dict(zip(my_dict.values(), my_dict.keys()))
  • swapped = dict(map(reversed, my_dict.items()))
print(swapped)
# Out: {a: 1, b: 2, c: 3}
Python 2.x 2.3

Als uw woordenboek groot is, kunt u overwegen itertools te importeren en izip of imap .


Woordenboeken samenvoegen

Combineer woordenboeken en vervang eventueel oude waarden met een ingebouwd woordenboekbegrip.

dict1 = {'w': 1, 'x': 1}
dict2 = {'x': 2, 'y': 2, 'z': 2}

{k: v for d in [dict1, dict2] for k, v in d.items()}
# Out: {'w': 1, 'x': 2, 'y': 2, 'z': 2}

Uitpakken van woordenboeken ( PEP 448 ) kan echter de voorkeur hebben.

Python 3.x 3.5
{**dict1, **dict2}
# Out: {'w': 1, 'x': 2, 'y': 2, 'z': 2}

Opmerking : woordenboekbegrippen werden toegevoegd in Python 3.0 en teruggezet naar 2.7+, in tegenstelling tot lijstbegrippen, die werden toegevoegd in 2.0. Versies <2.7 kunnen generatoruitdrukkingen en de ingebouwde dict() om het gedrag van woordenboekbegrippen te simuleren.

Generatoruitdrukkingen

Generator-uitdrukkingen lijken sterk op lijstbegrippen. Het belangrijkste verschil is dat het niet in één keer een volledige set resultaten oplevert; het creëert een generatorobject dat vervolgens kan worden herhaald.

Zie bijvoorbeeld het verschil in de volgende code:

# list comprehension
[x**2 for x in range(10)]
# Output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Python 2.x 2.4
# generator comprehension
(x**2 for x in xrange(10))
# Output: <generator object <genexpr> at 0x11b4b7c80>

Dit zijn twee heel verschillende objecten:

  • de lijstcomprehensie retourneert een list object dat de generator een begrip geeft generator .

  • generator kunnen niet worden geïndexeerd en maken gebruik van de next functie om items op volgorde te krijgen.

Opmerking : we gebruiken xrange omdat het ook een generatorobject maakt. Als we bereik zouden gebruiken, zou er een lijst worden gemaakt. xrange bestaat ook alleen in een latere versie van python 2. In python 3 retourneert range alleen een generator. Zie voor meer informatie het voorbeeld Verschillen tussen bereik en xrange-functies .


Python 2.x 2.4
g = (x**2 for x in xrange(10))
print(g[0])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'generator' object has no attribute '__getitem__'

g.next()  # 0
g.next()  # 1
g.next()  # 4
...
g.next()  # 81

g.next()  # Throws StopIteration Exception
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
Python 3.x 3.0

OPMERKING: De functie g.next() moet worden vervangen door next(g) en xrange met range omdat Iterator.next() en xrange() niet bestaan in Python 3.


Hoewel beide op dezelfde manier kunnen worden herhaald:

for i in [x**2 for x in range(10)]:
    print(i)

"""
Out:
0
1
4
...
81
"""
Python 2.x 2.4
for i in (x**2 for x in xrange(10)):
    print(i)

"""
Out:
0
1
4
.
.
.
81
"""

Gebruik cases

Generator-expressies worden lui geëvalueerd, wat betekent dat ze elke waarde alleen genereren en retourneren wanneer de generator wordt herhaald. Dit is vaak handig bij het doorlopen van grote gegevenssets, waardoor het niet nodig is om een duplicaat van de gegevensset in het geheugen te maken:

for square in (x**2 for x in range(1000000)):
    #do something

Een ander veel voorkomend geval is om iteratie over een hele iteratie te voorkomen als dit niet nodig is. In dit voorbeeld wordt een item opgehaald uit een externe API bij elke iteratie van get_objects() . Duizenden objecten kunnen bestaan, moeten één voor één worden opgehaald en we hoeven alleen te weten of een object dat overeenkomt met een patroon bestaat. Door een generatoruitdrukking te gebruiken wanneer we een object tegenkomen dat overeenkomt met het patroon.

def get_objects():
    """Gets objects from an API one by one"""
    while True:
        yield get_next_item()

def object_matches_pattern(obj):
    # perform potentially complex calculation
    return matches_pattern

def right_item_exists():
    items = (object_matched_pattern(each) for each in get_objects())
    for item in items:
        if item.is_the_right_one:


            return True
    return False

Begrip instellen

Setbegrip is vergelijkbaar met lijst- en woordenboekbegrip , maar het produceert een set , een ongeordende verzameling unieke elementen.

Python 2.x 2.7
# A set containing every value in range(5):
{x for x in range(5)}
# Out: {0, 1, 2, 3, 4}

# A set of even numbers between 1 and 10:
{x for x in range(1, 11) if x % 2 == 0}
# Out: {2, 4, 6, 8, 10}

# Unique alphabetic characters in a string of text:
text = "When in the Course of human events it becomes necessary for one people..."
{ch.lower() for ch in text if ch.isalpha()}
# Out: set(['a', 'c', 'b', 'e', 'f', 'i', 'h', 'm', 'l', 'o',
#           'n', 'p', 's', 'r', 'u', 't', 'w', 'v', 'y'])

Live demonstratie

Houd er rekening mee dat sets niet zijn geordend. Dit betekent dat de volgorde van de resultaten in de set kan verschillen van die in de bovenstaande voorbeelden.

Opmerking : Setbegrip is beschikbaar sinds python 2.7+, in tegenstelling tot lijstbegrippen, die zijn toegevoegd in 2.0. In Python 2.2 tot Python 2.6 kan de functie set() worden gebruikt met een generatoruitdrukking om hetzelfde resultaat te produceren:

Python 2.x 2.2
set(x for x in range(5))
# Out: {0, 1, 2, 3, 4}

Vermijd repetitieve en dure bewerkingen met voorwaardelijke clausule

Overweeg het onderstaande lijstbegrip:

>>> def f(x):
...     import time
...     time.sleep(.1)       # Simulate expensive function
...     return x**2

>>> [f(x) for x in range(1000) if f(x) > 10]
[16, 25, 36, ...]

Dit resulteert in twee aanroepen naar f(x) voor 1.000 waarden van x : een aanroep voor het genereren van de waarde en de andere voor het controleren van de if voorwaarde. Als f(x) een bijzonder dure operatie is, kan dit aanzienlijke gevolgen hebben voor de prestaties. Erger nog, als het aanroepen van f() bijwerkingen heeft, kan dit verrassende resultaten hebben.

In plaats daarvan moet u de dure bewerking voor elke waarde van x slechts eenmaal evalueren door een tussentijdse iterabele ( generatoruitdrukking ) als volgt te genereren :

>>> [v for v in (f(x) for x in range(1000)) if v > 10]
[16, 25, 36, ...]

Of, met behulp van de ingebouwde kaart equivalent:

>>> [v for v in map(f, range(1000)) if v > 10]
[16, 25, 36, ...]

Een andere manier die zou kunnen resulteren in een beter leesbare code is om het gedeeltelijke resultaat ( v in het vorige voorbeeld) in een iterabele (zoals een lijst of een tuple) te plaatsen en er vervolgens overheen te itereren. Aangezien v het enige element in de iterabele is, is het resultaat dat we nu een verwijzing hebben naar de uitvoer van onze langzame functie die slechts eenmaal wordt berekend:

>>> [v for x in range(1000) for v in [f(x)] if v > 10]
[16, 25, 36, ...]

In de praktijk kan de logica van code echter ingewikkelder zijn en het is belangrijk om deze leesbaar te houden. Over het algemeen wordt een afzonderlijke generatorfunctie aanbevolen boven een complexe one-liner:

>>> def process_prime_numbers(iterable):
...     for x in iterable:
...         if is_prime(x):
...             yield f(x)
...
>>> [x for x in process_prime_numbers(range(1000)) if x > 10]
[11, 13, 17, 19, ...]

Een andere manier om te voorkomen dat f(x) meerdere keren wordt berekend, is door de decorateur @functools.lru_cache() (Python 3.2+) op f(x) . Op deze manier, omdat de uitvoer van f voor de invoer x al eenmaal is berekend, zal de tweede functie-aanroep van het oorspronkelijke lijstbegrip net zo snel zijn als een woordenboekopzoekactie. Deze aanpak gebruikt memo om de efficiëntie te verbeteren, wat vergelijkbaar is met het gebruik van generatoruitdrukkingen.


Stel dat je een lijst moet afvlakken

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]

Sommige van de methoden kunnen zijn:

reduce(lambda x, y: x+y, l)

sum(l, [])

list(itertools.chain(*l))

Lijstbegrip zou echter de beste tijdcomplexiteit bieden.

[item for sublist in l for item in sublist]

De sneltoetsen op basis van + (inclusief het impliciete gebruik in totaal) zijn noodzakelijkerwijs O (L ^ 2) wanneer er L-sublijsten zijn - aangezien de tussentijdse resultatenlijst steeds langer wordt, wordt bij elke stap een nieuw tussentijdse resultatenlijstobject toegewezen, en alle items in het vorige tussenresultaat moeten worden gekopieerd (evenals een paar nieuwe toegevoegd aan het einde). Dus (voor de eenvoud en zonder daadwerkelijk verlies van algemeenheid) stel dat je L-sublijsten van I-items hebt: de eerste I-items worden L-1 keer heen en weer gekopieerd, de tweede I-items L-2 keer, enzovoort; totaal aantal exemplaren is I maal de som van x voor x van 1 tot en met L uitgesloten, dat wil zeggen I * (L ** 2) / 2.

Het lijstbegrip genereert slechts één lijst, en kopieert elk item (van de oorspronkelijke woonplaats naar de resultatenlijst) ook precies één keer.

Begrip met tupels

De for clausule van een lijstbegrip kan meer dan één variabele specificeren:

[x + y for x, y in [(1, 2), (3, 4), (5, 6)]]
# Out: [3, 7, 11]

[x + y for x, y in zip([1, 3, 5], [2, 4, 6])]
# Out: [3, 7, 11]

Dit is net als normaal for lussen:

for x, y in [(1,2), (3,4), (5,6)]:
    print(x+y)
# 3
# 7
# 11

Merk echter op dat als de uitdrukking die begint met het begrip een tupel is, deze tussen haakjes moet staan:

[x, y for x, y in [(1, 2), (3, 4), (5, 6)]]
# SyntaxError: invalid syntax

[(x, y) for x, y in [(1, 2), (3, 4), (5, 6)]]
# Out: [(1, 2), (3, 4), (5, 6)]

Voorvallen tellen met behulp van begrip

Wanneer we het aantal items in een iterabele, dat aan een voorwaarde voldoet, willen tellen, kunnen we begrip gebruiken om een idiomatische syntaxis te produceren:

# Count the numbers in `range(1000)` that are even and contain the digit `9`:
print (sum(
    1 for x in range(1000) 
    if x % 2 == 0 and
    '9' in str(x)
))
# Out: 95

Het basisconcept kan worden samengevat als:

  1. Herhaal over de elementen in range(1000) .
  2. Aaneenschakelen alle benodigde if omstandigheden.
  3. Gebruik 1 als uitdrukking om een 1 te retourneren voor elk item dat aan de voorwaarden voldoet.
  4. Vat alle 1 's samen om het aantal items te bepalen dat aan de voorwaarden voldoet.

Opmerking : hier verzamelen we niet de 1 's in een lijst (let op de afwezigheid van vierkante haakjes), maar we geven deze direct door aan de sum die ze opsomt. Dit wordt een generatoruitdrukking genoemd , die vergelijkbaar is met een begrip .

Typen in een lijst wijzigen

Kwantitatieve gegevens worden vaak ingelezen als strings die vóór verwerking moeten worden omgezet in numerieke typen. De typen van alle lijstitems kunnen worden geconverteerd met een Lijstbegrip of de map() -functie .

# Convert a list of strings to integers.
items = ["1","2","3","4"]
[int(item) for item in items]
# Out: [1, 2, 3, 4]

# Convert a list of strings to float.
items = ["1","2","3","4"]
map(float, items)
# Out:[1.0, 2.0, 3.0, 4.0] 


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