Stellen Sie sich vor, Sie müssen eine Protokolldatei mit mehreren Gigabyte verarbeiten. Die einfachste Lösung besteht darin, die gesamte Datei in den Speicher zu laden, in Zeilen aufzuteilen und mit der Verarbeitung zu beginnen. Aber was ist, wenn der Arbeitsspeicher nicht ausreicht? Oder wenn es Dutzende solcher Dateien gibt? Genau für solche Situationen gibt es in Python Generatoren und Iteratoren – leistungsstarke Werkzeuge, mit denen Sie mit großen Datenmengen arbeiten können, ohne sie vollständig in den Speicher zu laden.
Was sind Iteratoren?
Ein Iterator ist ein Objekt, mit dem Sie die Elemente einer Sammlung einzeln durchlaufen können, ohne die gesamte Sammlung auf einmal in den Speicher zu laden. In Python ist jedes Objekt, das die Methode __iter__() und __next__() implementiert, ein Iterator.
Wenn Sie for item in collection schreiben, ruft Python implizit iter(collection) auf, um einen Iterator zu erhalten, und ruft dann wiederholt next() auf, um das nächste Element zu erhalten, bis eine Ausnahme StopIteration ausgelöst wird.
# Beispiel eines einfachen Iterators
class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
# Anwendung
counter = CountDown(5)
for num in counter:
print(num) # Zeigt an: 5, 4, 3, 2, 1
Generatoren: Iteratoren auf Steroiden
Generatoren sind eine vereinfachte Möglichkeit, Iteratoren zu erstellen. Anstatt eine Klasse mit den Methoden __iter__() und __next__() zu schreiben, erstellen Sie einfach eine Funktion mit dem Schlüsselwort yield. Der Generator speichert seinen Zustand automatisch zwischen den Aufrufen und setzt die Ausführung dort fort, wo er aufgehört hat.
def count_down(start):
while start > 0:
yield start
start -= 1
# Anwendung
for num in count_down(5):
print(num) # Zeigt an: 5, 4, 3, 2, 1Der Vorteil liegt auf der Hand: Der Code ist kompakter und klarer geworden, die Funktionalität ist gleich geblieben.
Warum spart das Speicherplatz?
Der Hauptvorteil von Generatoren ist die Lazy-Evaluation. Elemente werden nur dann erstellt, wenn sie benötigt werden, und können sofort verarbeitet und vergessen werden. Schauen wir uns ein konkretes Beispiel an:
# Schlecht: Wir laden alles in den Speicher
def read_large_file_bad(file_path):
with open(file_path, 'r') as file:
lines = file.readlines() # Die gesamte Datei ist im Speicher!
return [line.strip().upper() for line in lines]
# Gut: Wir verarbeiten zeilenweise
def read_large_file_good(file_path):
with open(file_path, 'r') as file:
for line in file: # file ist bereits ein Iterator!
yield line.strip().upper()
# Anwendung
for processed_line in read_large_file_good('huge_log.txt'):
# Wir bearbeiten eine Zeile nach der anderen
process(processed_line)Im ersten Fall, wenn die Datei 2 GB wiegt, wird Ihr Programm mindestens 2 GB RAM belegen. Im zweiten Fall nur wenige Kilobytes, unabhängig von der Dateigröße.

Praktische Anwendungsbeispiele.
Verarbeitung von Daten aus der API
APIs geben Daten oft seitenweise zurück. Der Generator kann diese Komplexität verbergen:
def fetch_all_users(api_client, page_size=100):
page = 1
while True:
users = api_client.get_users(page=page, size=page_size)
if not users:
break
for user in users:
yield user
page += 1
# Anwendung
for user in fetch_all_users(api):
print(user['name']) # Wir bearbeiten die Nutzer einzelnUnendliche Folgen
Mit Generatoren können Sie Endlosschleifen erstellen, ohne dass die Gefahr eines Speicherüberlaufs besteht:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# Sie können so viele Elemente nehmen, wie Sie benötigen
from itertools import islice
first_ten = list(islice(fibonacci(), 10))
print(first_ten) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]Datenverarbeitungsketten
Generatoren eignen sich hervorragend für die Erstellung von Verarbeitungsketten:
def read_csv(file_path):
with open(file_path) as f:
for line in f:
yield line.strip().split(',')
def filter_by_status(rows, status):
for row in rows:
if row[2] == status: # Status in der dritten Spalte
yield row
def extract_emails(rows):
for row in rows:
yield row[1] # E-Mail in der zweiten Spalte
# Wir bauen das Förderband zusammen
rows = read_csv('users.csv')
active_users = filter_by_status(rows, 'active')
emails = extract_emails(active_users)
# Erst hier beginnt die eigentliche Verarbeitung
for email in emails:
send_notification(email)Das Schöne an diesem Ansatz ist, dass jede Stufe des Förderbands unabhängig und testbar ist und der Speicher nur für ein Element verwendet wird.
Ausdrucksgeneratoren
Neben Generatorfunktionen unterstützt Python Generatorausdrücke - eine kompakte Syntax zum Erstellen von Generatoren:
# List comprehension - erstellt eine Liste im Speicher
squares_list = [x**2 for x in range(1000000)] # Nimmt viel Speicherplatz ein
# Generatorausdruck - erstellt einen Generator
squares_gen = (x**2 for x in range(1000000)) # Nimmt fast keinen Speicherplatz ein
# Kann in Funktionen verwendet werden
total = sum(x**2 for x in range(1000000)) # Effektiv!Bitte beachten Sie: Statt eckigen Klammern werden runde Klammern verwendet. Dies ist eine der einfachsten und effektivsten Änderungen, die Sie an Ihrem Code vornehmen können, um Speicherplatz zu sparen.
Wann sollten Generatoren NICHT verwendet werden?
Trotz aller Vorteile sind Generatoren nicht immer geeignet:
Wenn Sie mehrfache Zugriffe auf Daten benötigen: Der Generator kann nur einmal durchlaufen werden. Wenn Sie mehrmals auf Daten zugreifen müssen, ist es besser, eine Liste zu verwenden.
Wenn die Zugriffsgeschwindigkeit wichtig ist: Das Element wird sofort nach Index aus der Liste abgerufen, und der Generator muss von Anfang an sortiert werden.
Wenn Daten in den Speicher gelegt werden: Wenn Sie einen kleinen Datensatz haben, sind die Gemeinkosten für die Erstellung eines Generators möglicherweise nicht gerechtfertigt.
Wenn Sie die Länge wissen müssen: len() funktioniert nicht mit Generatoren, und die Berechnung der Länge erfordert eine vollständige Suche.
Fortgeschrittene Techniken.
Delegieren von Generatoren
Mit Hilfe von yield from kann die Ausführung an einen anderen Generator delegiert werden:
def read_multiple_files(*file_paths):
for path in file_paths:
with open(path) as f:
yield from f # Zeilenweise Lesung delegieren
# Wir lesen mehrere Dateien als einen Stream
for line in read_multiple_files('log1.txt', 'log2.txt', 'log3.txt'):
process(line)Zwei-Wege-Kommunikation mit Generatoren
Generatoren können nicht nur Werte über yield zurückgeben, sondern sie auch über die Methode send() übernehmen:
def running_average():
total = 0
count = 0
average = None
while True:
value = yield average
total += value
count += 1
average = total / count
# Anwendung
avg = running_average()
next(avg) # Wir starten den Generator
print(avg.send(10)) # 10.0
print(avg.send(20)) # 15.0
print(avg.send(30)) # 20.0Wir messen die Effizienz
Schauen wir uns den tatsächlichen Unterschied in der Speichernutzung an:
import sys
# Liste
numbers_list = [x for x in range(1000000)]
print(f"List: {sys.getsizeof(numbers_list) / 1024 / 1024:.2f} MB")
# Generator
numbers_gen = (x for x in range(1000000))
print(f"Generator: {sys.getsizeof(numbers_gen) / 1024:.2f} KB")Auf meinem Computer zeigt das Ergebnis einen tausendfachen Unterschied: Die Liste belegt etwa 8 MB und der Generator weniger als 0,1 KB.
Befund
Generatoren und Iteratoren sind nicht nur syntaktischer Zucker, sondern ein grundlegendes Werkzeug für die Arbeit mit großen Datenmengen. Sie ermöglichen es Ihnen, effizienteren Code zu schreiben, der sich an Daten jeder Größe anpasst. Zu verstehen, wann und wie man Generatoren verwendet, unterscheidet einen erfahrenen Entwickler von einem Anfänger.
Fangen Sie einfach an: Ersetzen Sie List Comprehensions durch Generator Expressions, wenn Sie keinen mehrfachen Zugriff auf Daten benötigen. Versuchen Sie dann, eine Generatorfunktion für die Dateiverarbeitung zu schreiben. Und nach und nach werden Sie feststellen, dass Sie über Datenströme nachdenken, nicht über Sammlungen im Speicher.
Anlage Kodik bietet strukturierte Kurse für Einsteiger in die Entwicklung, die Python, JavaScript, HTML, CSS und viele andere Technologien abdecken.
Schließen Sie sich unserem Telegram-Kanal, wo Sie Community-Support, zusätzliche Ressourcen und Antworten auf Fragen von erfahrenen Entwicklern finden.
Lernen Sie Programmieren effektiv und mit Freude!
