Generatorer i Python
Denna artikel förklarar generatorer i Python.
YouTube Video
Generatorer i Python
Översikt
Python-generatorer är en typ av iterator och en kraftfull funktion för att utföra upprepande bearbetning effektivt. De gör det möjligt att skriva minneseffektiv kod när du arbetar med stora datamängder.
Vad är en generator?
En generator i Python är en speciell funktion som producerar ett värde åt gången, definierad med nyckelordet yield
. Dess kännetecken är att den pausar exekveringen men behåller sitt tillstånd och kan återupptas senare.
Grunderna i yield
yield
är ett nyckelord som returnerar ett värde och pausar funktionsexekveringen samtidigt.
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 anropas returnerar funktionen ett generatorobjekt som avger värden ett i taget.
- Om du anropar
next()
när det inte finns något nästa värde, kommer ettStopIteration
-fel att uppstå.
next()
och 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")
- Genom att hantera
StopIteration
-felet explicit på detta sätt kan du upptäcka när en generator har avslutats.
send(value)
Att anropa send(value)
återupptar generatorn och skickar value
till positionen för yield
-uttrycket. Det skickade värdet kan tas emot på generatorsidan som returvärdet från yield
-uttrycket. Vid det första anropet kan du inte skicka något annat än None
med send(value)
, så du måste använda 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 generatornsyield
ett uttryck som returnerar 10, och 10 tilldelasx
.
throw()
Att anropa throw
återupptar generatorn och kastar ett undantag vid positionen för den pausade yield
. Du kan hantera undantaget inne i generatorn för att fortsätta bearbetningen. Om undantaget inte fångas, propagerar det utåt och generatorn avslutas.
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 denna kod anropas
throw
för att injicera ett undantag i generatorn. På generatorsidan hanteras undantaget ochrecovered
returneras.
close()
Att anropa close()
avslutar generatorn. Inne i generatorn kan du utföra städning med hjälp av finally
. Att anropa next()
eller send()
efter att ha anropat close()
ger ett StopIteration
-fel.
1def gen():
2 try:
3 yield 1
4 finally:
5 print("Cleaning up...")
6
7g = gen()
8print(next(g)) # -> 1
9g.close() # -> Cleaning up...
- Denna kod visar att anrop av
close()
avslutar generatorn och utlöser städrutinen ifinally
.
yield from
yield from
är en syntax som används för delegering till en subgenerator. Det är ett enkelt sätt att anropa en annan generator inuti en generator och skicka vidare alla dess värden till det yttre omfånget.
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]
- Den här koden delegerar alla värden från subgeneratorn till den yttre generatorn med hjälp av
yield from
, och returnerar sedan3
.
Relation till iteratorer
Generatorer implementerar internt __iter__()
och __next__()
, vilket gör dem till en typ av iterator. Därför är de helt kompatibla med itererbara operationer såsom for
-loopar.
Integration med for
-loopar
I Python använder en for
-loop internt next()
för att automatiskt hämta värden.
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6for value in simple_generator():
7 print(value)
Med denna metod hanteras även StopIteration
automatiskt.
Skapa oändliga 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 är möjligt att skapa oändliga loopar, men man måste vara försiktig vid användning.
Generatorexpressioner
Generatorexpressioner, skrivna med parenteser, gör det möjligt att definiera generatorer med en syntax liknande listförstå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)
Till skillnad från listförståelser laddar de inte in alla element i minnet på en gång, vilket gör dem mer minneseffektiva.
Felhantering i generatorer
Undantag kan uppstå inuti en generator. I sådana fall använder du try-except
precis som i vanlig Python-kod.
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 det här exemplet utförs korrekt felhantering vid division med noll.
Stackspårning för en generator
Om ett undantag uppstår inuti generatorn kommer det att kastas när generatorn återupptas.
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)
- Denna generator returnerar först
1
. Felet som kastas vid återupptagande fångas och visas som ett felmeddelande.
Exempel på användning av generatorer
Läsa en fil rad för rad (lämpligt för stora filer)
1def read_large_file(filepath):
2 with open(filepath, 'r') as f:
3 for line in f:
4 yield line.strip()
- Denna funktion läser en textfil rad för rad med en iterator, tar bort blanksteg från varje rad och returnerar den som en generator, vilket gör det möjligt att bearbeta stora filer med låg minnesanvändning.
Generator för Fibonaccitalföljden
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)
- Denna kod använder en generator för att sekventiellt generera Fibonaccital mindre än den övre gränsen och skriver ut dem med en
for
-loop.
Användningsområden
Generatorer kan också användas i följande scenarier.
- Sekventiell bearbetning av stora CSV- eller loggfiler
- API-paginering
- Bearbeta strömmande data (t.ex. Kafka, IoT-enheter)
Sammanfattning
Koncept | Nyckelpunkt |
---|---|
yield |
Pausar och returnerar ett värde |
Generatorfunktion | En funktion som innehåller yield och returnerar en iterator när den anropas |
Fördelar | Minneseffektivt och idealiskt för bearbetning av stora datamängder |
Generatorexpression | Tillåter en kortfattad syntax såsom (x for x in iterable) |
Genom att använda generatorer kan du effektivt bearbeta stora datamängder samtidigt som du sparar minne och håller din kod kortfattad.
Du kan följa med i artikeln ovan med hjälp av Visual Studio Code på vår YouTube-kanal. Vänligen kolla även in YouTube-kanalen.