Entdecken des Speichermodells für First-Class-Objekte in Python

PythonPythonBeginner
Jetzt üben

This tutorial is from open-source community. Access the source code

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

In diesem Lab werden Sie das Konzept der First - Class - Objekte in Python kennenlernen und sein Speichermodell untersuchen. Python behandelt Funktionen, Typen und Daten als First - Class - Objekte, was leistungsstarke und flexible Programmiermuster ermöglicht.

Sie werden auch wiederverwendbare Hilfsfunktionen für die Verarbeitung von CSV - Daten erstellen. Insbesondere werden Sie eine allgemeine Funktion zum Lesen von CSV - Daten in der Datei reader.py erstellen, die in verschiedenen Projekten wiederverwendet werden kann.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/BasicConceptsGroup(["Basic Concepts"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ModulesandPackagesGroup(["Modules and Packages"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python(("Python")) -.-> python/FileHandlingGroup(["File Handling"]) python(("Python")) -.-> python/PythonStandardLibraryGroup(["Python Standard Library"]) python/BasicConceptsGroup -.-> python/strings("Strings") python/BasicConceptsGroup -.-> python/type_conversion("Type Conversion") python/FunctionsGroup -.-> python/function_definition("Function Definition") python/FunctionsGroup -.-> python/scope("Scope") python/ModulesandPackagesGroup -.-> python/importing_modules("Importing Modules") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") python/FileHandlingGroup -.-> python/file_operations("File Operations") python/PythonStandardLibraryGroup -.-> python/data_collections("Data Collections") subgraph Lab Skills python/strings -.-> lab-132489{{"Entdecken des Speichermodells für First-Class-Objekte in Python"}} python/type_conversion -.-> lab-132489{{"Entdecken des Speichermodells für First-Class-Objekte in Python"}} python/function_definition -.-> lab-132489{{"Entdecken des Speichermodells für First-Class-Objekte in Python"}} python/scope -.-> lab-132489{{"Entdecken des Speichermodells für First-Class-Objekte in Python"}} python/importing_modules -.-> lab-132489{{"Entdecken des Speichermodells für First-Class-Objekte in Python"}} python/classes_objects -.-> lab-132489{{"Entdecken des Speichermodells für First-Class-Objekte in Python"}} python/file_operations -.-> lab-132489{{"Entdecken des Speichermodells für First-Class-Objekte in Python"}} python/data_collections -.-> lab-132489{{"Entdecken des Speichermodells für First-Class-Objekte in Python"}} end

Das Verständnis von First-Class-Objekten in Python

In Python wird alles als Objekt behandelt. Dies umfasst auch Funktionen und Typen. Was bedeutet das? Nun, es bedeutet, dass Sie Funktionen und Typen in Datenstrukturen speichern, sie als Argumente an andere Funktionen übergeben und sogar von anderen Funktionen zurückgeben können. Dies ist ein sehr mächtiges Konzept, und wir werden es anhand der Verarbeitung von CSV-Daten als Beispiel untersuchen.

Die Erkundung von First-Class-Typen

Zunächst starten wir den Python-Interpreter. Öffnen Sie ein neues Terminal in der WebIDE und geben Sie den folgenden Befehl ein. Dieser Befehl startet den Python-Interpreter, in dem wir unseren Python-Code ausführen werden.

python3

Wenn Sie in Python mit CSV-Dateien arbeiten, müssen Sie oft die Zeichenketten, die Sie aus diesen Dateien lesen, in die entsprechenden Datentypen umwandeln. Beispielsweise kann eine Zahl in einer CSV-Datei als Zeichenkette gelesen werden, aber wir möchten sie in unserem Python-Code als Ganzzahl oder Fließkommazahl verwenden. Um dies zu tun, können wir eine Liste von Konvertierungsfunktionen erstellen.

coltypes = [str, int, float]

Beachten Sie, dass wir eine Liste erstellen, die die eigentlichen Typfunktionen enthält, nicht Zeichenketten. In Python sind Typen First-Class-Objekte, was bedeutet, dass wir sie wie jedes andere Objekt behandeln können. Wir können sie in Listen packen, herumreichen und in unserem Code verwenden.

Nun lesen wir einige Daten aus einer Portfolio-CSV-Datei, um zu sehen, wie wir diese Konvertierungsfunktionen verwenden können.

import csv
f = open('portfolio.csv')
rows = csv.reader(f)
headers = next(rows)
row = next(rows)
print(row)

Wenn Sie diesen Code ausführen, sollten Sie eine Ausgabe ähnlich der folgenden sehen. Dies ist die erste Datenzeile aus der CSV-Datei, dargestellt als Liste von Zeichenketten.

['AA', '100', '32.20']

Als Nächstes verwenden wir die zip-Funktion. Die zip-Funktion nimmt mehrere Iterierbare (wie Listen oder Tupel) und paart ihre Elemente. Wir werden sie verwenden, um jeden Wert aus der Zeile mit seiner entsprechenden Typkonvertierungsfunktion zu paaren.

r = list(zip(coltypes, row))
print(r)

Dies wird die folgende Ausgabe produzieren. Jedes Paar enthält eine Typfunktion und einen Zeichenkettenwert aus der CSV-Datei.

[(<class 'str'>, 'AA'), (<class 'int'>, '100'), (<class 'float'>, '32.20')]

Jetzt, da wir diese Paare haben, können wir jede Funktion anwenden, um die Werte in ihre entsprechenden Typen umzuwandeln.

record = [func(val) for func, val in zip(coltypes, row)]
print(record)

Die Ausgabe wird zeigen, dass die Werte in ihre entsprechenden Typen umgewandelt wurden. Die Zeichenkette 'AA' bleibt eine Zeichenkette, '100' wird zur Ganzzahl 100 und '32.20' wird zur Fließkommazahl 32.2.

['AA', 100, 32.2]

Wir können diese Werte auch mit ihren Spaltennamen kombinieren, um ein Wörterbuch zu erstellen. Ein Wörterbuch ist eine nützliche Datenstruktur in Python, die es uns ermöglicht, Schlüssel-Wert-Paare zu speichern.

record_dict = dict(zip(headers, record))
print(record_dict)

Die Ausgabe wird ein Wörterbuch sein, in dem die Schlüssel die Spaltennamen und die Werte die konvertierten Daten sind.

{'name': 'AA', 'shares': 100, 'price': 32.2}

Sie können all diese Schritte in einer einzigen Komprehension ausführen. Eine Komprehension ist eine kompakte Möglichkeit, Listen, Wörterbücher oder Mengen in Python zu erstellen.

result = {name: func(val) for name, func, val in zip(headers, coltypes, row)}
print(result)

Die Ausgabe wird dasselbe Wörterbuch wie zuvor sein.

{'name': 'AA', 'shares': 100, 'price': 32.2}

Wenn Sie mit der Arbeit im Python-Interpreter fertig sind, können Sie ihn beenden, indem Sie den folgenden Befehl eingeben.

exit()

Diese Demonstration zeigt, wie die Behandlung von Funktionen als First-Class-Objekte in Python leistungsstarke Datenverarbeitungstechniken ermöglicht. Indem wir Typen und Funktionen als Objekte behandeln können, können wir flexibleres und kompakteres Code schreiben.

Erstellen einer Hilfsfunktion für die CSV-Verarbeitung

Nachdem wir nun verstehen, wie Python's First-Class-Objekte uns bei der Datenkonvertierung helfen können, werden wir eine wiederverwendbare Hilfsfunktion erstellen. Diese Funktion wird CSV-Daten lesen und sie in eine Liste von Wörterbüchern umwandeln. Dies ist eine sehr nützliche Operation, da CSV-Dateien häufig zur Speicherung tabellarischer Daten verwendet werden, und die Umwandlung in eine Liste von Wörterbüchern erleichtert die Arbeit mit den Daten in Python.

Erstellen der CSV-Leser-Hilfsfunktion

Zunächst öffnen Sie die WebIDE. Sobald sie geöffnet ist, navigieren Sie zum Projektverzeichnis und erstellen Sie eine neue Datei mit dem Namen reader.py. In dieser Datei werden wir eine Funktion definieren, die CSV-Daten liest und Typkonvertierungen anwendet. Typkonvertierungen sind wichtig, da die Daten in einer CSV-Datei normalerweise als Zeichenketten gelesen werden, aber wir möglicherweise andere Datentypen wie Ganzzahlen oder Fließkommazahlen für die weitere Verarbeitung benötigen.

Fügen Sie den folgenden Code in reader.py ein:

import csv

def read_csv_as_dicts(filename, types):
    """
    Read a CSV file into a list of dictionaries, converting each field according
    to the types provided.

    Parameters:
    filename (str): Name of the CSV file to read
    types (list): List of type conversion functions for each column

    Returns:
    list: List of dictionaries representing the CSV data
    """
    records = []
    with open(filename, 'r') as f:
        rows = csv.reader(f)
        headers = next(rows)  ## Get the column headers

        for row in rows:
            ## Apply type conversions to each value in the row
            converted_row = [func(val) for func, val in zip(types, row)]

            ## Create a dictionary mapping headers to converted values
            record = dict(zip(headers, converted_row))
            records.append(record)

    return records

Diese Funktion öffnet zunächst die angegebene CSV-Datei. Dann liest sie die Kopfzeilen der CSV-Datei, die die Namen der Spalten sind. Danach geht sie jede Zeile in der Datei durch. Für jeden Wert in der Zeile wendet sie die entsprechende Typkonvertierungsfunktion aus der types-Liste an. Schließlich erstellt sie ein Wörterbuch, in dem die Schlüssel die Spaltenüberschriften und die Werte die konvertierten Daten sind, und fügt dieses Wörterbuch der records-Liste hinzu. Sobald alle Zeilen verarbeitet sind, gibt sie die records-Liste zurück.

Testen der Hilfsfunktion

Lassen Sie uns unsere Hilfsfunktion testen. Zunächst öffnen Sie ein Terminal und starten Sie einen Python-Interpreter, indem Sie Folgendes eingeben:

python3

Jetzt, da wir im Python-Interpreter sind, können wir unsere Funktion verwenden, um die Portfolio-Daten zu lesen. Die Portfolio-Daten sind eine CSV-Datei, die Informationen über Aktien enthält, wie den Namen der Aktie, die Anzahl der Anteile und den Preis.

import reader
portfolio = reader.read_csv_as_dicts('portfolio.csv', [str, int, float])
for record in portfolio[:3]:  ## Show the first 3 records
    print(record)

Wenn Sie diesen Code ausführen, sollten Sie eine Ausgabe ähnlich der folgenden sehen:

{'name': 'AA', 'shares': 100, 'price': 32.2}
{'name': 'IBM', 'shares': 50, 'price': 91.1}
{'name': 'CAT', 'shares': 150, 'price': 83.44}

Diese Ausgabe zeigt die ersten drei Datensätze aus den Portfolio-Daten, wobei die Datentypen korrekt konvertiert wurden.

Lassen Sie uns auch unsere Funktion mit den CTA-Bus-Daten testen. Die CTA-Bus-Daten sind eine andere CSV-Datei, die Informationen über Buslinien, Daten, Tagestypen und die Anzahl der Fahrten enthält.

rows = reader.read_csv_as_dicts('ctabus.csv', [str, str, str, int])
print(f"Total rows: {len(rows)}")
print("First row:", rows[0])

Die Ausgabe sollte in etwa so aussehen:

Total rows: 577563
First row: {'route': '3', 'date': '01/01/2001', 'daytype': 'U', 'rides': 7354}

Dies zeigt, dass unsere Funktion verschiedene CSV-Dateien verarbeiten und die entsprechenden Typkonvertierungen anwenden kann.

Um den Python-Interpreter zu beenden, geben Sie ein:

exit()

Sie haben nun eine wiederverwendbare Hilfsfunktion erstellt, die jede CSV-Datei lesen und die entsprechenden Typkonvertierungen anwenden kann. Dies zeigt die Stärke von Python's First-Class-Objekten und wie sie zur Erstellung flexiblen, wiederverwendbaren Codes genutzt werden können.

✨ Lösung prüfen und üben

Die Erkundung des Python-Speichermodells

Das Python-Speichermodell spielt eine entscheidende Rolle bei der Bestimmung, wie Objekte im Speicher gespeichert und referenziert werden. Das Verständnis dieses Modells ist besonders wichtig, wenn Sie mit großen Datensätzen arbeiten, da es die Leistung und den Speicherverbrauch Ihrer Python-Programme erheblich beeinflussen kann. In diesem Schritt werden wir uns speziell darauf konzentrieren, wie Zeichenkettenobjekte in Python behandelt werden, und Möglichkeiten zur Optimierung des Speicherverbrauchs für große Datensätze untersuchen.

Zeichenkettenwiederholungen in Datensätzen

Die CTA-Bus-Daten enthalten viele wiederholte Werte, wie z.B. Routennamen. Wiederholte Werte in einem Datensatz können zu einem ineffizienten Speicherverbrauch führen, wenn sie nicht richtig behandelt werden. Um das Ausmaß dieses Problems zu verstehen, lassen Sie uns zunächst untersuchen, wie viele eindeutige Routenzeichenketten es im Datensatz gibt.

Zunächst öffnen Sie einen Python-Interpreter. Sie können dies tun, indem Sie den folgenden Befehl in Ihrem Terminal ausführen:

python3

Sobald der Python-Interpreter geöffnet ist, laden wir die CTA-Bus-Daten und finden die eindeutigen Routen. Hier ist der Code, um dies zu erreichen:

import reader
rows = reader.read_csv_as_dicts('ctabus.csv', [str, str, str, int])

## Find unique route names
routes = {row['route'] for row in rows}
print(f"Number of unique route names: {len(routes)}")

In diesem Code importieren wir zunächst das reader-Modul, das vermutlich eine Funktion zum Lesen von CSV-Dateien als Wörterbücher enthält. Dann verwenden wir die read_csv_as_dicts-Funktion, um die Daten aus der ctabus.csv-Datei zu laden. Das zweite Argument [str, str, str, int] gibt die Datentypen für jede Spalte in der CSV-Datei an. Danach verwenden wir eine Mengenkomprehension, um alle eindeutigen Routennamen im Datensatz zu finden und geben die Anzahl der eindeutigen Routennamen aus.

Die Ausgabe sollte sein:

Number of unique route names: 181

Jetzt überprüfen wir, wie viele verschiedene Zeichenkettenobjekte für diese Routen erstellt werden. Auch wenn es nur 181 eindeutige Routennamen gibt, kann Python für jedes Vorkommen eines Routennamens im Datensatz ein neues Zeichenkettenobjekt erstellen. Um dies zu überprüfen, verwenden wir die id()-Funktion, um die eindeutige Kennung jedes Zeichenkettenobjekts zu erhalten.

## Count unique string object IDs
routeids = {id(row['route']) for row in rows}
print(f"Number of unique route string objects: {len(routeids)}")

Die Ausgabe mag Sie überraschen:

Number of unique route string objects: 542305

Dies zeigt, dass es nur 181 eindeutige Routennamen gibt, aber über 500.000 eindeutige Zeichenkettenobjekte. Dies geschieht, weil Python für jede Zeile ein neues Zeichenkettenobjekt erstellt, auch wenn die Werte gleich sind. Dies kann zu einem erheblichen Speicherverschwendung führen, insbesondere wenn Sie mit großen Datensätzen arbeiten.

Zeichenketten-Interning zur Speichersparnis

Python bietet eine Möglichkeit, Zeichenketten zu "internieren" (wiederzuverwenden) mithilfe der sys.intern()-Funktion. Das Zeichenketten-Interning kann Speicher sparen, wenn Sie viele doppelte Zeichenketten in Ihrem Datensatz haben. Wenn Sie eine Zeichenkette internieren, überprüft Python, ob bereits eine identische Zeichenkette im Intern-Pool existiert. Wenn dies der Fall ist, gibt es eine Referenz auf das vorhandene Zeichenkettenobjekt zurück, anstatt ein neues zu erstellen.

Lassen Sie uns demonstrieren, wie das Zeichenketten-Interning mit einem einfachen Beispiel funktioniert:

import sys

## Without interning
a = 'hello world'
b = 'hello world'
print(f"a is b (without interning): {a is b}")

## With interning
a = sys.intern(a)
b = sys.intern(b)
print(f"a is b (with interning): {a is b}")

In diesem Code erstellen we zunächst zwei Zeichenkettenvariablen a und b mit demselben Wert ohne Interning. Der is-Operator überprüft, ob zwei Variablen auf dasselbe Objekt verweisen. Ohne Interning sind a und b verschiedene Objekte, daher gibt a is b False zurück. Dann internieren wir beide Zeichenketten mit sys.intern(). Nach dem Interning verweisen a und b auf dasselbe Objekt im Intern-Pool, daher gibt a is b True zurück.

Die Ausgabe sollte sein:

a is b (without interning): False
a is b (with interning): True

Jetzt verwenden wir das Zeichenketten-Interning beim Lesen der CTA-Bus-Daten, um den Speicherverbrauch zu reduzieren. Wir verwenden auch das tracemalloc-Modul, um den Speicherverbrauch vor und nach dem Interning zu verfolgen.

import sys
import reader
import tracemalloc

## Start memory tracking
tracemalloc.start()

## Read data with interning for the route column
rows = reader.read_csv_as_dicts('ctabus.csv', [sys.intern, str, str, int])

## Check unique route objects again
routeids = {id(row['route']) for row in rows}
print(f"Number of unique route string objects (with interning): {len(routeids)}")

## 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")

In diesem Code starten wir zunächst die Speicherüberwachung mit tracemalloc.start(). Dann lesen wir die CTA-Bus-Daten mit Interning für die Routenspalte, indem wir sys.intern als Datentyp für die erste Spalte übergeben. Danach überprüfen wir erneut die Anzahl der eindeutigen Routenzeichenkettenobjekte und geben den aktuellen und maximalen Speicherverbrauch aus.

Die Ausgabe sollte in etwa so aussehen:

Number of unique route string objects (with interning): 181
Current memory usage: 189.56 MB
Peak memory usage: 209.32 MB

Lassen Sie uns den Interpreter neu starten und versuchen, sowohl die Routen- als auch die Datumszeichenketten zu internieren, um zu sehen, ob wir den Speicherverbrauch weiter reduzieren können.

exit()

Starten Sie Python erneut:

python3
import sys
import reader
import tracemalloc

## Start memory tracking
tracemalloc.start()

## Read data with interning for both route and date columns
rows = reader.read_csv_as_dicts('ctabus.csv', [sys.intern, sys.intern, str, int])

## Check memory usage
current, peak = tracemalloc.get_traced_memory()
print(f"Current memory usage (interning route and date): {current / 1024 / 1024:.2f} MB")
print(f"Peak memory usage (interning route and date): {peak / 1024 / 1024:.2f} MB")

Die Ausgabe sollte eine weitere Verringerung des Speicherverbrauchs zeigen:

Current memory usage (interning route and date): 170.23 MB
Peak memory usage (interning route and date): 190.05 MB

Dies zeigt, wie das Verständnis des Python-Speichermodells und die Verwendung von Techniken wie dem Zeichenketten-Interning helfen können, Ihre Programme zu optimieren, insbesondere wenn Sie mit großen Datensätzen mit wiederholten Werten arbeiten.

Schließlich beenden Sie den Python-Interpreter:

exit()

Spaltenorientierte Datenspeicherung

Bisher haben wir CSV-Daten als Liste von Zeilenwörterbüchern gespeichert. Das bedeutet, dass jede Zeile in der CSV-Datei als ein Wörterbuch dargestellt wird, wobei die Schlüssel die Spaltenüberschriften und die Werte die entsprechenden Daten in dieser Zeile sind. Bei der Arbeit mit großen Datensätzen kann diese Methode jedoch ineffizient sein. Die Speicherung von Daten in einem spaltenorientierten Format kann eine bessere Wahl sein. Bei einem spaltenorientierten Ansatz wird die Daten jeder Spalte in einer separaten Liste gespeichert. Dies kann den Speicherverbrauch erheblich reduzieren, da ähnliche Datentypen zusammengefasst werden, und es kann auch die Leistung für bestimmte Operationen wie die Aggregation von Daten nach Spalten verbessern.

Erstellen eines spaltenorientierten Datenlesers

Jetzt werden wir eine neue Datei erstellen, die uns helfen wird, CSV-Daten in einem spaltenorientierten Format zu lesen. Erstellen Sie eine neue Datei mit dem Namen colreader.py im Projektverzeichnis mit dem folgenden Code:

import csv

class DataCollection:
    def __init__(self, headers, columns):
        """
        Initialize a column-oriented data collection.

        Parameters:
        headers (list): Column header names
        columns (dict): Dictionary mapping header names to column data lists
        """
        self.headers = headers
        self.columns = columns
        self._length = len(columns[headers[0]]) if headers else 0

    def __len__(self):
        """Return the number of rows in the collection."""
        return self._length

    def __getitem__(self, index):
        """
        Get a row by index, presented as a dictionary.

        Parameters:
        index (int): Row index

        Returns:
        dict: Dictionary representing the row at the given index
        """
        if isinstance(index, int):
            if index < 0 or index >= self._length:
                raise IndexError("Index out of range")

            return {header: self.columns[header][index] for header in self.headers}
        else:
            raise TypeError("Index must be an integer")

def read_csv_as_columns(filename, types):
    """
    Read a CSV file into a column-oriented data structure, converting each field
    according to the types provided.

    Parameters:
    filename (str): Name of the CSV file to read
    types (list): List of type conversion functions for each column

    Returns:
    DataCollection: Column-oriented data collection representing the CSV data
    """
    with open(filename, 'r') as f:
        rows = csv.reader(f)
        headers = next(rows)  ## Get the column headers

        ## Initialize columns
        columns = {header: [] for header in headers}

        ## Read data into columns
        for row in rows:
            ## Convert values according to the specified types
            converted_values = [func(val) for func, val in zip(types, row)]

            ## Add each value to its corresponding column
            for header, value in zip(headers, converted_values):
                columns[header].append(value)

    return DataCollection(headers, columns)

Dieser Code macht zwei wichtige Dinge:

  1. Er definiert eine DataCollection-Klasse. Diese Klasse speichert Daten in Spalten, ermöglicht es uns aber, auf die Daten zuzugreifen, als ob es sich um eine Liste von Zeilenwörterbüchern handeln würde. Dies ist nützlich, da es einen vertrauten Weg bietet, mit den Daten zu arbeiten.
  2. Er definiert eine read_csv_as_columns-Funktion. Diese Funktion liest CSV-Daten aus einer Datei und speichert sie in einer spaltenorientierten Struktur. Sie konvertiert auch jedes Feld in der CSV-Datei gemäß den von uns angegebenen Typen.

Testen des spaltenorientierten Lesers

Lassen Sie uns unseren spaltenorientierten Leser mit den CTA-Bus-Daten testen. Zunächst öffnen Sie einen Python-Interpreter. Sie können dies tun, indem Sie den folgenden Befehl in Ihrem Terminal ausführen:

python3

Sobald der Python-Interpreter geöffnet ist, führen Sie den folgenden Code aus:

import colreader
import tracemalloc
from sys import intern

## Start memory tracking
tracemalloc.start()

## Read data into column-oriented structure with string interning
data = colreader.read_csv_as_columns('ctabus.csv', [intern, intern, intern, int])

## Check that we can access the data like a list of dictionaries
print(f"Number of rows: {len(data)}")
print("First 3 rows:")
for i in range(3):
    print(data[i])

## 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")

Die Ausgabe sollte so aussehen:

Number of rows: 577563
First 3 rows:
{'route': '3', 'date': '01/01/2001', 'daytype': 'U', 'rides': 7354}
{'route': '4', 'date': '01/01/2001', 'daytype': 'U', 'rides': 9288}
{'route': '6', 'date': '01/01/2001', 'daytype': 'U', 'rides': 6048}
Current memory usage: 38.67 MB
Peak memory usage: 103.42 MB

Jetzt vergleichen wir dies mit unserem vorherigen zeilenorientierten Ansatz. Führen Sie den folgenden Code im gleichen Python-Interpreter aus:

import reader
import tracemalloc
from sys import intern

## Reset memory tracking
tracemalloc.reset_peak()

## Read data into row-oriented structure with string interning
rows = reader.read_csv_as_dicts('ctabus.csv', [intern, intern, intern, int])

## Check memory usage
current, peak = tracemalloc.get_traced_memory()
print(f"Current memory usage (row-oriented): {current / 1024 / 1024:.2f} MB")
print(f"Peak memory usage (row-oriented): {peak / 1024 / 1024:.2f} MB")

Die Ausgabe sollte in etwa so aussehen:

Current memory usage (row-oriented): 170.23 MB
Peak memory usage (row-oriented): 190.05 MB

Wie Sie sehen können, verwendet der spaltenorientierte Ansatz erheblich weniger Speicher!

Lassen Sie uns auch testen, ob wir die Daten weiterhin wie zuvor analysieren können. Führen Sie den folgenden Code aus:

## Find all unique routes in the column-oriented data
routes = {row['route'] for row in data}
print(f"Number of unique routes: {len(routes)}")

## Count rides per route (first 5)
from collections import defaultdict
route_rides = defaultdict(int)
for row in data:
    route_rides[row['route']] += row['rides']

## Show the top 5 routes by total rides
top_routes = sorted(route_rides.items(), key=lambda x: x[1], reverse=True)[:5]
print("Top 5 routes by total rides:")
for route, rides in top_routes:
    print(f"Route {route}: {rides:,} rides")

Die Ausgabe sollte sein:

Number of unique routes: 181
Top 5 routes by total rides:
Route 9: 158,545,826 rides
Route 49: 129,872,910 rides
Route 77: 120,086,065 rides
Route 79: 109,348,708 rides
Route 4: 91,405,538 rides

Schließlich beenden Sie den Python-Interpreter, indem Sie den folgenden Befehl ausführen:

exit()

Wir können sehen, dass der spaltenorientierte Ansatz nicht nur Speicher spart, sondern auch die gleichen Analysen wie zuvor ermöglicht. Dies zeigt, wie unterschiedliche Datenspeicherstrategien einen erheblichen Einfluss auf die Leistung haben können, während sie weiterhin die gleiche Schnittstelle für die Arbeit mit den Daten bieten.

✨ Lösung prüfen und üben

Zusammenfassung

In diesem Lab haben Sie mehrere wichtige Python-Konzepte gelernt. Zunächst haben Sie verstanden, wie Python Funktionen, Typen und andere Entitäten als First-Class-Objekte behandelt, wodurch sie wie normale Daten weitergegeben und gespeichert werden können. Zweitens haben Sie wiederverwendbare Hilfsfunktionen für die Verarbeitung von CSV-Daten mit automatischer Typkonvertierung erstellt.

Darüber hinaus haben Sie das Python-Speichermodell untersucht und das Zeichenketten-Interning (String Interning) verwendet, um den Speicherverbrauch für wiederholte Daten zu reduzieren. Sie haben auch eine effizientere spaltenorientierte Speichermethode für große Datensätze implementiert, die eine vertraute Benutzeroberfläche bietet. Diese Konzepte zeigen die Flexibilität und Stärke von Python bei der Datenverarbeitung auf, und die Techniken können auf reale Datenanalyseprojekte angewendet werden.