Einführung
In diesem Lab lernen Sie das grundlegende Konzept der Iteration in der Python-Programmierung kennen. Iteration ermöglicht es Ihnen, Elemente in Sequenzen wie Listen, Tupeln und Dictionaries effizient zu verarbeiten. Das Beherrschen von Iterationstechniken kann Ihre Fähigkeiten in der Python-Programmierung erheblich verbessern.
Sie werden mehrere leistungsstarke Python-Iterationstechniken erkunden, darunter die einfache Iteration mit einer for-Schleife, die Entpackung von Sequenzen, die Verwendung von eingebauten Funktionen wie enumerate() und zip() sowie die Nutzung von Generatorausdrücken für eine bessere Speichereffizienz.
Grundlegende Iteration und Sequenzentpackung
In diesem Schritt werden wir die grundlegende Iteration mit for-Schleifen und die Sequenzentpackung in Python untersuchen. Iteration ist ein grundlegendes Konzept in der Programmierung, das es Ihnen ermöglicht, jedes Element in einer Sequenz nacheinander zu durchlaufen. Die Sequenzentpackung hingegen ermöglicht es Ihnen, die einzelnen Elemente einer Sequenz bequem Variablen zuzuweisen.
Laden von Daten aus einer CSV-Datei
Beginnen wir damit, einige Daten aus einer CSV-Datei zu laden. CSV (Comma-Separated Values, deutsch: durch Kommas getrennte Werte) ist ein gängiges Dateiformat zur Speicherung tabellarischer Daten. Um zu beginnen, müssen wir im WebIDE ein Terminal öffnen und den Python-Interpreter starten. Dadurch können wir Python-Code interaktiv ausführen.
cd ~/project
python3
Jetzt, da wir im Python-Interpreter sind, können wir den folgenden Python-Code ausführen, um Daten aus der portfolio.csv-Datei zu lesen. Zunächst importieren wir das csv-Modul, das Funktionen für die Arbeit mit CSV-Dateien bereitstellt. Dann öffnen wir die Datei und erstellen ein csv.reader-Objekt, um die Daten zu lesen. Wir verwenden die next-Funktion, um die Spaltenüberschriften zu erhalten, und wandeln die verbleibenden Daten in eine Liste um. Schließlich verwenden wir die pprint-Funktion aus dem pprint-Modul, um die Zeilen in einem lesbareren Format auszugeben.
import csv
f = open('portfolio.csv')
f_csv = csv.reader(f)
headers = next(f_csv) ## Get the column headers
rows = list(f_csv) ## Convert the remaining data to a list
from pprint import pprint
pprint(rows) ## Pretty print the rows
Sie sollten eine Ausgabe ähnlich der folgenden sehen:
[['AA', '100', '32.20'],
['IBM', '50', '91.10'],
['CAT', '150', '83.44'],
['MSFT', '200', '51.23'],
['GE', '95', '40.37'],
['MSFT', '50', '65.10'],
['IBM', '100', '70.44']]
Grundlegende Iteration mit for-Schleifen
Die for-Anweisung in Python wird verwendet, um über jede Datensequenz wie eine Liste, ein Tupel oder einen String zu iterieren. In unserem Fall verwenden wir sie, um über die Zeilen der Daten zu iterieren, die wir aus der CSV-Datei geladen haben.
for row in rows:
print(row)
Dieser Code durchläuft jede Zeile in der rows-Liste und gibt sie aus. Sie werden jede Datenzeile aus unserer CSV-Datei nacheinander sehen.
['AA', '100', '32.20']
['IBM', '50', '91.10']
['CAT', '150', '83.44']
['MSFT', '200', '51.23']
['GE', '95', '40.37']
['MSFT', '50', '65.10']
['IBM', '100', '70.44']
Sequenzentpackung in Schleifen
Python ermöglicht es Ihnen, Sequenzen direkt in einer for-Schleife zu entpacken. Dies ist sehr nützlich, wenn Sie die Struktur jedes Elements in der Sequenz kennen. In unserem Fall enthält jede Zeile in der rows-Liste drei Elemente: einen Namen, die Anzahl der Aktien und den Preis. Wir können diese Elemente direkt in der for-Schleife entpacken.
for name, shares, price in rows:
print(name, shares, price)
Dieser Code entpackt jede Zeile in die Variablen name, shares und price und gibt sie dann aus. Sie werden die Daten in einem lesbareren Format sehen.
AA 100 32.20
IBM 50 91.10
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.10
IBM 100 70.44
Wenn Sie einige Werte nicht benötigen, können Sie _ als Platzhalter verwenden, um anzuzeigen, dass Sie diese Werte nicht interessieren. Beispielsweise können Sie, wenn Sie nur den Namen und den Preis ausgeben möchten, den folgenden Code verwenden:
for name, _, price in rows:
print(name, price)
Dieser Code ignoriert das zweite Element in jeder Zeile und gibt nur den Namen und den Preis aus.
AA 32.20
IBM 91.10
CAT 83.44
MSFT 51.23
GE 40.37
MSFT 65.10
IBM 70.44
Erweiterte Entpackung mit dem *-Operator
Für eine fortgeschrittenere Entpackung können Sie den *-Operator als Wildcard verwenden. Dies ermöglicht es Ihnen, mehrere Elemente in einer Liste zu sammeln. Lassen Sie uns unsere Daten anhand des Namens mit dieser Technik gruppieren.
from collections import defaultdict
byname = defaultdict(list)
for name, *data in rows:
byname[name].append(data)
## Print the data for IBM
print(byname['IBM'])
## Iterate through IBM's data
for shares, price in byname['IBM']:
print(shares, price)
In diesem Code importieren wir zunächst die defaultdict-Klasse aus dem collections-Modul. Ein defaultdict ist ein Wörterbuch, das automatisch einen neuen Wert (in diesem Fall eine leere Liste) erstellt, wenn der Schlüssel nicht existiert. Dann verwenden wir den *-Operator, um alle Elemente außer dem ersten in einer Liste namens data zu sammeln. Wir speichern diese Liste im byname-Wörterbuch, gruppiert nach dem Namen. Schließlich geben wir die Daten für IBM aus und durchlaufen sie, um die Anzahl der Aktien und den Preis auszugeben.
Ausgabe:
[['50', '91.10'], ['100', '70.44']]
50 91.10
100 00.44
In diesem Beispiel sammelt *data alle Elemente außer dem ersten in einer Liste, die wir dann in einem nach Namen gruppierten Wörterbuch speichern. Dies ist eine leistungsstarke Technik zur Verarbeitung von Daten mit Sequenzen variabler Länge.
Verwendung der Funktionen enumerate() und zip()
In diesem Schritt werden wir zwei äußerst nützliche eingebaute Funktionen in Python erkunden, die für die Iteration unerlässlich sind: enumerate() und zip(). Diese Funktionen können Ihren Code erheblich vereinfachen, wenn Sie mit Sequenzen arbeiten.
Zählen mit enumerate()
Wenn Sie über eine Sequenz iterieren, müssen Sie oft den Index oder die Position jedes Elements verfolgen. Hier kommt die enumerate()-Funktion ins Spiel. Die enumerate()-Funktion nimmt eine Sequenz als Eingabe und gibt für jedes Element in dieser Sequenz Paare aus (Index, Wert) zurück.
Wenn Sie im Python-Interpreter aus dem vorherigen Schritt weitergefahren sind, können Sie ihn weiterhin verwenden. Wenn nicht, starten Sie eine neue Sitzung. Hier ist, wie Sie die Daten einrichten können, wenn Sie neu beginnen:
## If you're starting a new session, reload the data first:
## import csv
## f = open('portfolio.csv')
## f_csv = csv.reader(f)
## headers = next(f_csv)
## rows = list(f_csv)
## Use enumerate to get row numbers
for rowno, row in enumerate(rows):
print(rowno, row)
Wenn Sie den obigen Code ausführen, erzeugt die enumerate(rows)-Funktion Paare aus einem Index (beginnend bei 0) und der entsprechenden Zeile aus der rows-Sequenz. Die for-Schleife entpackt dann diese Paare in die Variablen rowno und row, und wir geben sie aus.
Ausgabe:
0 ['AA', '100', '32.20']
1 ['IBM', '50', '91.10']
2 ['CAT', '150', '83.44']
3 ['MSFT', '200', '51.23']
4 ['GE', '95', '40.37']
5 ['MSFT', '50', '65.10']
6 ['IBM', '100', '70.44']
Wir können den Code noch lesbarer gestalten, indem wir enumerate() mit Entpackung kombinieren. Die Entpackung ermöglicht es uns, die Elemente einer Sequenz direkt einzelnen Variablen zuzuweisen.
for rowno, (name, shares, price) in enumerate(rows):
print(rowno, name, shares, price)
In diesem Code verwenden wir ein zusätzliches Klammerpaar um (name, shares, price), um die Zeilendaten richtig zu entpacken. Die enumerate(rows)-Funktion gibt uns immer noch den Index und die Zeile, aber jetzt entpacken wir die Zeile in die Variablen name, shares und price.
Ausgabe:
0 AA 100 32.20
1 IBM 50 91.10
2 CAT 150 83.44
3 MSFT 200 51.23
4 GE 95 40.37
5 MSFT 50 65.10
6 IBM 100 70.44
Paarung von Daten mit zip()
Die zip()-Funktion ist ein weiteres leistungsstarkes Werkzeug in Python. Sie wird verwendet, um entsprechende Elemente aus mehreren Sequenzen zu kombinieren. Wenn Sie mehrere Sequenzen an zip() übergeben, erstellt es einen Iterator, der Tupel erzeugt, wobei jedes Tupel Elemente aus jeder der Eingabesequenzen an der gleichen Position enthält.
Schauen wir uns an, wie wir zip() mit den headers- und row-Daten verwenden können, mit denen wir gearbeitet haben.
## Recall the headers variable from earlier
print(headers) ## Should show ['name', 'shares', 'price']
## Get the first row
row = rows[0]
print(row) ## Should show ['AA', '100', '32.20']
## Use zip to pair column names with values
for col, val in zip(headers, row):
print(col, val)
In diesem Code nimmt zip(headers, row) die headers-Sequenz und die row-Sequenz und paart ihre entsprechenden Elemente. Die for-Schleife entpackt dann diese Paare in col (für den Spaltennamen aus headers) und val (für den Wert aus row), und wir geben sie aus.
Ausgabe:
['name', 'shares', 'price']
['AA', '100', '32.20']
name AA
shares 100
price 32.20
Eine sehr häufige Verwendung von zip() besteht darin, Wörterbücher aus Schlüssel-Wert-Paaren zu erstellen. In Python ist ein Wörterbuch eine Sammlung von Schlüssel-Wert-Paaren.
## Create a dictionary from headers and row values
record = dict(zip(headers, row))
print(record)
Hier erstellt zip(headers, row) Paare aus Spaltennamen und Werten, und die dict()-Funktion nimmt diese Paare und wandelt sie in ein Wörterbuch um.
Ausgabe:
{'name': 'AA', 'shares': '100', 'price': '32.20'}
Wir können diese Idee erweitern, um alle Zeilen in unserer rows-Sequenz in Wörterbücher umzuwandeln.
## Convert all rows to dictionaries
for row in rows:
record = dict(zip(headers, row))
print(record)
In dieser Schleife verwenden wir für jede Zeile in rows zip(headers, row), um Schlüssel-Wert-Paare zu erstellen, und dann dict(), um diese Paare in ein Wörterbuch umzuwandeln. Diese Technik ist in Datenverarbeitungsanwendungen sehr verbreitet, insbesondere wenn Sie mit CSV-Dateien arbeiten, bei denen die erste Zeile die Überschriften enthält.
Ausgabe:
{'name': 'AA', 'shares': '100', 'price': '32.20'}
{'name': 'IBM', 'shares': '50', 'price': '91.10'}
{'name': 'CAT', 'shares': '150', 'price': '83.44'}
{'name': 'MSFT', 'shares': '200', 'price': '51.23'}
{'name': 'GE', 'shares': '95', 'price': '40.37'}
{'name': 'MSFT', 'shares': '50', 'price': '65.10'}
{'name': 'IBM', 'shares': '100', 'price': '70.44'}
Generatorausdrücke und Speichereffizienz
In diesem Schritt werden wir Generatorausdrücke erkunden. Diese sind unglaublich nützlich, wenn Sie in Python mit großen Datensätzen arbeiten. Sie können Ihren Code viel speichereffizienter machen, was von entscheidender Bedeutung ist, wenn Sie mit einer großen Datenmenge arbeiten.
Verständnis von Generatorausdrücken
Ein Generatorausdruck ähnelt einer Listenkomprehension, aber es gibt einen wichtigen Unterschied. Wenn Sie eine Listenkomprehension verwenden, erstellt Python eine Liste mit allen Ergebnissen auf einmal. Dies kann viel Speicherplatz beanspruchen, insbesondere wenn Sie mit einem großen Datensatz arbeiten. Ein Generatorausdruck hingegen erzeugt die Ergebnisse nacheinander, sobald sie benötigt werden. Das bedeutet, dass er nicht alle Ergebnisse auf einmal im Speicher speichern muss, was eine beträchtliche Menge an Speicherplatz sparen kann.
Schauen wir uns ein einfaches Beispiel an, um zu verstehen, wie dies funktioniert:
## Start a new Python session if needed
## python3
## List comprehension (creates a list in memory)
nums = [1, 2, 3, 4, 5]
squares_list = [x*x for x in nums]
print(squares_list)
## Generator expression (creates a generator object)
squares_gen = (x*x for x in nums)
print(squares_gen) ## This doesn't print the values, just the generator object
## Iterate through the generator to get values
for n in squares_gen:
print(n)
Wenn Sie diesen Code ausführen, sehen Sie die folgende Ausgabe:
[1, 4, 9, 16, 25]
<generator object <genexpr> at 0x7f...>
1
4
9
16
25
Eine wichtige Eigenschaft von Generatoren ist, dass sie nur einmal durchlaufen werden können. Sobald Sie alle Werte in einem Generator durchlaufen haben, ist er erschöpft, und Sie können die Werte nicht erneut abrufen.
## Try to iterate again over the same generator
for n in squares_gen:
print(n) ## Nothing will be printed, as the generator is already exhausted
Sie können auch manuell nacheinander Werte aus einem Generator abrufen, indem Sie die next()-Funktion verwenden.
## Create a fresh generator
squares_gen = (x*x for x in nums)
## Get values one by one
print(next(squares_gen)) ## 1
print(next(squares_gen)) ## 4
print(next(squares_gen)) ## 9
Wenn es keine weiteren Werte im Generator gibt, löst der Aufruf von next() eine StopIteration-Ausnahme aus.
Generatorfunktionen mit yield
Für komplexere Generatorlogik können Sie Generatorfunktionen mit der yield-Anweisung schreiben. Eine Generatorfunktion ist eine spezielle Art von Funktion, die yield verwendet, um Werte nacheinander zurückzugeben, anstatt ein einzelnes Ergebnis auf einmal zurückzugeben.
def squares(nums):
for x in nums:
yield x*x
## Use the generator function
for n in squares(nums):
print(n)
Wenn Sie diesen Code ausführen, sehen Sie die folgende Ausgabe:
1
4
9
16
25
Reduzierung des Speicherverbrauchs mit Generatorausdrücken
Jetzt schauen wir uns an, wie Generatorausdrücke den Speicherverbrauch sparen können, wenn Sie mit großen Datensätzen arbeiten. Wir verwenden die CTA-Busdatendatei, die recht groß ist.
Zunächst versuchen wir einen speicherintensiven Ansatz:
import tracemalloc
tracemalloc.start()
import readrides
rows = readrides.read_rides_as_dicts('ctabus.csv')
rt22 = [row for row in rows if row['route'] == '22']
max_row = max(rt22, key=lambda row: int(row['rides']))
print(max_row)
## Check memory usage
current, peak = tracemalloc.get_traced_memory()
print(f"Current memory usage: {current / 1024 / 1024:.2f} MB")
print(f"Peak memory usage: {peak / 1024 / 1024:.2f} MB")
Jetzt beenden Sie Python und starten Sie es neu, um es mit einem Generator-basierten Ansatz zu vergleichen:
exit() python3
import tracemalloc
tracemalloc.start()
import csv
f = open('ctabus.csv')
f_csv = csv.reader(f)
headers = next(f_csv)
## Use generator expressions for all operations
rows = (dict(zip(headers, row)) for row in f_csv)
rt22 = (row for row in rows if row['route'] == '22')
max_row = max(rt22, key=lambda row: int(row['rides']))
print(max_row)
## Check memory usage
current, peak = tracemalloc.get_traced_memory()
print(f"Current memory usage: {current / 1024 / 1024:.2f} MB")
print(f"Peak memory usage: {peak / 1024 / 1024:.2f} MB")
Sie sollten einen deutlichen Unterschied im Speicherverbrauch zwischen diesen beiden Ansätzen bemerken. Der Generator-basierte Ansatz verarbeitet die Daten schrittweise, ohne alles auf einmal in den Speicher zu laden, was viel speichereffizienter ist.
Generatorausdrücke mit Reduktionsfunktionen
Generatorausdrücke sind besonders nützlich, wenn sie mit Funktionen wie sum(), min(), max(), any() und all() kombiniert werden, die eine gesamte Sequenz verarbeiten und ein einzelnes Ergebnis produzieren.
Schauen wir uns einige Beispiele an:
from readport import read_portfolio
portfolio = read_portfolio('portfolio.csv')
## Calculate the total value of the portfolio
total_value = sum(s['shares']*s['price'] for s in portfolio)
print(f"Total portfolio value: {total_value}")
## Find the minimum number of shares in any holding
min_shares = min(s['shares'] for s in portfolio)
print(f"Minimum shares in any holding: {min_shares}")
## Check if any stock is IBM
has_ibm = any(s['name'] == 'IBM' for s in portfolio)
print(f"Portfolio contains IBM: {has_ibm}")
## Check if all stocks are IBM
all_ibm = all(s['name'] == 'IBM' for s in portfolio)
print(f"All stocks are IBM: {all_ibm}")
## Count IBM shares
ibm_shares = sum(s['shares'] for s in portfolio if s['name'] == 'IBM')
print(f"Total IBM shares: {ibm_shares}")
Generatorausdrücke sind auch nützlich für Zeichenkettenoperationen. Hier ist, wie Sie Werte in einem Tupel verbinden können:
s = ('GOOG', 100, 490.10)
## This would fail: ','.join(s)
## Use a generator expression to convert all items to strings
joined = ','.join(str(x) for x in s)
print(joined) ## Output: 'GOOG,100,490.1'
Der Hauptvorteil der Verwendung von Generatorausdrücken in diesen Beispielen ist, dass keine Zwischenlisten erstellt werden, was zu einem speichereffizienteren Code führt.
Zusammenfassung
In diesem Lab haben Sie mehrere leistungsstarke Python-Iterationstechniken gelernt. Zunächst haben Sie die grundlegende Iteration und die Sequenzentpackung beherrscht, indem Sie for-Schleifen verwendet haben, um über Sequenzen zu iterieren und sie in einzelne Variablen zu entpacken. Zweitens haben Sie eingebaute Funktionen wie enumerate() erkundet, um Indizes während der Iteration zu verfolgen, und zip(), um Elemente aus verschiedenen Sequenzen zu paaren.
Diese Techniken sind grundlegend für effizientes Python-Programmieren. Sie ermöglichen es Ihnen, kürzeren, lesbareren und speichereffizienteren Code zu schreiben. Indem Sie diese Iterationsmuster beherrschen, können Sie Datenverarbeitungstasks in Ihren Python-Projekten effektiver bewältigen.