Generatorer i Python
Denne artikkelen forklarer generatorer i Python.
YouTube Video
Generatorer i Python
Oversikt
Python-generatorer er en type iterator og en kraftig funksjon for å utføre repeterende prosessering effektivt. De gjør det mulig å skrive minneeffektiv kode når du håndterer store datamengder.
Hva er en generator?
En generator i Python er en spesiell funksjon som genererer én verdi om gangen, definert ved å bruke yield
-nøkkelordet. Dens kjennetegn er at den pauser kjøringen samtidig som den beholder sin tilstand, og kan gjenoppta senere.
Grunnleggende om yield
yield
er et nøkkelord som returnerer en verdi og pauser funksjonen samtidig.
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6gen = simple_generator()
7
8print(next(gen)) # 1
9print(next(gen)) # 2
10print(next(gen)) # 3
- Når den kalles, returnerer denne funksjonen et generatorobjekt som leverer verdier én etter én.
- Hvis du kaller
next()
når det ikke finnes en neste verdi, vil enStopIteration
-feil oppstå.
next()
og StopIteration
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6gen = simple_generator()
7
8try:
9 while True:
10 value = next(gen)
11 print(value)
12except StopIteration:
13 print("Finished")
- Ved å eksplisitt håndtere
StopIteration
-feilen på denne måten, kan du oppdage når en generator er ferdig.
send(value)
Å kalle send(value)
gjenopptar generatoren og sender value
til posisjonen ved yield
-uttrykket. Den sendte verdien kan mottas på generatorsiden som returneringsverdien av yield
-uttrykket. Ved det første kall kan du ikke sende noe annet enn None
med send(value)
, så du må bruke next()
eller send(None)
.
1def gen():
2 x = yield 1
3 print(f"x = {x}")
4 y = yield 2
5 print(f"y = {y}")
6
7g = gen()
8print(next(g)) # -> 1 (value from yield 1)
9print(g.send(10)) # -> x = 10, 2 (value from yield 2)
10print(g.send(20)) # -> y = 20, StopIteration occurs
- Med
send(10)
blir generatorensyield
et uttrykk som returnerer 10, og 10 blir tildelt tilx
.
throw()
Å kalle throw
gjenopptar generatoren og løfter et unntak ved posisjonen der yield
er pauset. Du kan håndtere unntaket inne i generatoren for å fortsette behandlingen. Hvis unntaket ikke fanges, propageres det utover og generatoren avsluttes.
1def gen():
2 try:
3 yield 1
4 except ValueError as e:
5 print(f"Caught: {e}")
6 yield "recovered"
7
8g = gen()
9print(next(g)) # -> 1
10print(g.throw(ValueError("boom"))) # -> Caught: boom, "recovered"
- I denne koden blir
throw
kalt for å sette inn et unntak i generatoren. På generatorsiden blir unntaket håndtert ogrecovered
returneres.
close()
Å kalle close()
avslutter generatoren. Inne i generatoren kan du foreta opprydding ved å bruke finally
. Å kalle next()
eller send()
etter at du har kalt close()
utløser en StopIteration
-feil.
1def gen():
2 try:
3 yield 1
4 finally:
5 print("Cleaning up...")
6
7g = gen()
8print(next(g)) # -> 1
9g.close() # -> Cleaning up...
- Denne koden viser at å kalle
close()
avslutter generatoren og utløser oppryddingsprosessen ifinally
.
yield from
yield from
er en syntaks som brukes for å delegere til en subgenerator. Det er en enkel måte å kalle en annen generator inni en generator og gi alle dens verdier videre til ytre nivå.
1def sub_gen():
2 yield 1
3 yield 2
4
5def main_gen():
6 yield from sub_gen()
7 yield 3
8
9print(list(main_gen())) # -> [1, 2, 3]
- Denne koden delegerer alle verdiene fra subgeneratoren til den ytre generatoren ved å bruke
yield from
, og deretter gir den3
.
Forholdet til iteratorer
Generatorer implementerer internt __iter__()
og __next__()
, og er dermed en type iterator. De er derfor fullt kompatible med operasjoner over iterable objekter, som for
-løkker.
Integrering med for
-løkker
I Python bruker en for
-løkke internt next()
for automatisk å hente verdier.
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6for value in simple_generator():
7 print(value)
Med denne metoden skjer håndteringen av StopIteration
automatisk.
Opprette uendelige generatorer
1def count_up(start=0):
2 while True:
3 yield start
4 start += 1
5
6counter = count_up()
7print(next(counter)) # 0
8print(next(counter)) # 1
Det er mulig å lage uendelige løkker, men man må være forsiktig ved bruk av dem.
Generatoruttrykk
Generatoruttrykk, skrevet med parenteser, lar deg definere generatorer med en syntaks lik listeforståelser.
1# List comprehension (generates the entire list at once)
2squares_list = [x**2 for x in range(5)]
3print(squares_list)
4
5# Generator expression
6squares_gen = (x**2 for x in range(5))
7for square in squares_gen:
8 print(square)
I motsetning til listeforståelser lastes ikke alle elementene inn i minnet samtidig, noe som gjør dem mer minneeffektive.
Feilhåndtering i generatorer
Unntak kan oppstå inne i en generator. I slike tilfeller bruker du try-except
slik du ellers gjør i Python-kode.
1def safe_divide_generator(numbers, divisor):
2 """Yields results of dividing numbers by a given divisor safely."""
3 for number in numbers:
4 try:
5 yield number / divisor # Attempt to divide and yield result.
6 except ZeroDivisionError:
7 yield float('inf') # Return infinity if division by zero occurs.
8
9# Example usage
10numbers = [10, 20, 30]
11gen = safe_divide_generator(numbers, 0) # Create generator with divisor as 0.
12for value in gen:
13 print(value) # Output: inf, inf, inf
I dette eksemplet utføres korrekt feilhåndtering ved deling på null.
Stakksporing for en generator
Hvis et unntak oppstår inne i generatoren, vil det bli kastet når generatoren gjenopptas.
1def error_generator():
2 """A generator that yields values and raises an error."""
3 yield 1
4 raise ValueError("An error occurred") # Raise a ValueError intentionally.
5 yield 2
6
7gen = error_generator()
8print(next(gen)) # Output: 1 (first value yielded)
9try:
10 print(next(gen)) # Attempt to get the next value, which raises an error
11except ValueError as e:
12 print(e) # Output: An error occurred (exception message is printed)
- Denne generatoren returnerer
1
først. Feilen som oppstår ved gjenopptakelse fanges opp og vises som en feilmelding.
Eksempler på bruk av generatorer
Lese en fil linje for linje (egnet for store filer)
1def read_large_file(filepath):
2 with open(filepath, 'r') as f:
3 for line in f:
4 yield line.strip()
- Denne funksjonen leser en tekstfil linje for linje med en iterator, fjerner mellomrom fra hver linje, og returnerer det som en generator, noe som gjør at store filer kan behandles med lavt minneforbruk.
Generator for Fibonacci-sekvensen
1def fibonacci(limit):
2 a, b = 0, 1
3 while a < limit:
4 yield a
5 a, b = b, a + b
6
7for n in fibonacci(100):
8 print(n)
- Denne koden bruker en generator til å generere Fibonacci-tall mindre enn øvre grense i rekkefølge, og skriver dem ut med en
for
-løkke.
Bruksområder
Generatorer kan også brukes i følgende scenarioer.
- Sekvensiell prosessering av store CSV- eller loggfiler
- Paginering av API
- Behandling av strømmende data (f.eks. Kafka, IoT-enheter)
Sammendrag
Konsept | Viktig punkt |
---|---|
yield |
Pauser og returnerer en verdi |
Generatorfunksjon | En funksjon som inneholder yield og returnerer en iterator når den kalles |
Fordeler | Minneeffektiv og ideell for behandling av store datasett |
Generatoruttrykk | Gir en konsis syntaks som (x for x in iterable) |
Ved å bruke generatorer kan du effektivt behandle store datasett samtidig som du sparer minne og holder koden din konsis.
Du kan følge med på artikkelen ovenfor ved å bruke Visual Studio Code på vår YouTube-kanal. Vennligst sjekk ut YouTube-kanalen.