Achtergrond
Python heeft vanaf versie 2.4 lijst-expressies (list comprehensions) aangevuld met zogenaamde "generator-expressies". Ze maken het mogelijk om een luiere reeks waarden te creëren, vergelijkbaar met generatoren, maar in een compacte en leesbare vorm.
Probleem
Lijst-expressies ([x for x in iterable]) creëren een lijst door meteen alle elementen in het geheugen te laden. Dit is inefficiënt of zelfs gevaarlijk als het aantal elementen erg groot is. Generatorfuncties (met gebruik van yield) zijn flexibeler, maar vereisen een aparte functie-definitie en meer regels code.
Oplossing
Generator-expressies ((x for x in iterable)) bieden een beknopte syntaxis voor het produceren van reeksen lui (elementen worden berekend wanneer dat nodig is, in plaats van allemaal tegelijk te worden geladen). Ze lijken op lijst-expressies, maar gebruiken ronde haakjes:
# Lijst-expressie laadt alles in het geheugen squares_list = [x**2 for x in range(10**6)] # Generator-expressie: elementen komen op aanvraag, geheugen wordt bijna niet gebruikt squares_gen = (x**2 for x in range(10**6)) # Ontvang de eerste vijf waarden van de generator for _ in range(5): print(next(squares_gen))
Belangrijke kenmerken:
Kan eenzelfde generator-expressie meerdere keren worden doorlopen?
Nee, na het itereren één keer is de generator "opgebraakt". Voor een herhaaldelijke doorgang moet je een nieuwe generator maken of gebruik maken van een lijst-expressie.
it = (x for x in range(3)) print(list(it)) # [0,1,2] print(list(it)) # [] — je kunt geen waarden meer krijgen
Bewaren generatoren de staat tussen gebruik?
Ja, een generator-expressie bewaart de "positie" tussen aanroepen van next() (of bij de volgende iteratie), maar kan niet worden teruggezet naar het begin, tenzij je een nieuw object maakt.
Kan een generator-expressie meerdere keren in één regel worden gebruikt?
Nee! Als je de generator tegelijk in meerdere plaatsen "uitpakt" (bijvoorbeeld in verschillende functies, zonder het terug te geven aan een lijst), gaat een deel van de gegevens verloren — elk gebruik verschuift de aanwijzer vooruit.
g = (x for x in range(3)) print(sum(g), list(g)) # sum(g) haalt alles, list(g) blijft leeg
In een project voor het analyseren van grote bestanden werd gebruikt:
data = (parse_line(line) for line in file) process(list(data)) other_process(list(data))
Voordelen:
Nadelen:
Gebruikten lijst-expressies als herhaaldelijk gebruik van gegevens vereist is, of creëerden een generator voor eenmalige consumptie:
# Generator alleen voor eenmalige analyse (bijvoorbeeld het berekenen van de som) total = sum(parse_line(line) for line in file)
Voordelen:
Nadelen: