Das Modul array stellt einen spezialisierten Datentyp array.array zur Speicherung von Sequenzen homogener Zahlen bereit. Im Gegensatz zur universellen list bieten array.array-Arrays eine effizientere Speichernutzung und eine höhere Leistung bei der Arbeit mit numerischen Daten.
📦 Hauptvorteile von array.array
Der Hauptunterschied zwischen array.array und list ist die kompakte Datenspeicherung. Anstelle einer Liste von Zeigern auf Python-Objekte speichert array.array Werte als zusammenhängenden Block von Bytes, was es ideal für die folgenden Aufgaben macht.
1. Speichereinsparungen bei der Arbeit mit großen Zahlensätzen
Bei der Verarbeitung von Millionen numerischer Elemente werden Speichereinsparungen entscheidend. array.array reduziert den Overhead erheblich.
import array
import sys
def compare_memory_usage(num_elements: int = 1_000_000) -> None:
"""
Vergleicht die Speichernutzung zwischen list und array.array.
Args:
num_elements (int, optional): Anzahl der Elemente für den Test.
Standardmäßig 1.000.000.
"""
# Erstellen einer Liste mit ganzzahligen Python-Objekten
list_numbers = list(range(num_elements))
# Erstellen eines Arrays, in dem Zahlen als 4-Byte-C-Typ-Ints gespeichert werden
array_numbers = array.array('i', range(num_elements))
list_size = sys.getsizeof(list_numbers)
array_size = sys.getsizeof(array_numbers)
print(f"Anzahl der Elemente: {num_elements}")
print(f"Größe der Liste: {list_size / 1024 / 1024:.2f} MB")
print(f"Größe des Arrays: {array_size / 1024 / 1024:.2f} MB")
if array_size > 0:
print(f"Speichereinsparung: {list_size / array_size:.2f}x")
# Beispiel für die Verwendung
if __name__ == "__main__":
compare_memory_usage()
Ausgabe:„`
Anzahl der Elemente: 1000000
Größe der Liste: 7.63 MB
Größe des Arrays: 3.82 MB
Speichereinsparung: 2.00x
---
### 2. Gesteigerte Leistung bei numerischen Operationen
Aufgrund der zusammenhängenden Speicherzuweisung werden mathematische Operationen mit `array.array`-Elementen schneller ausgeführt, da der Prozessor den Cache effizienter nutzen kann.
python
import array
import timeit
def compare_performance(num_elements: int = 10_000_000) -> None:
„““
Vergleicht die Leistung beim Summieren von Elementen in list und array.array.
Args:
num_elements (int, optional): Anzahl der Elemente für den Test.
Standardmäßig 10.000.000.
"""
setup_code = f"""
import array
data = range({num_elements})
list_data = list(data)
array_data = array.array(‚i‘, data)
„““
# Zeit für die Liste messen
list_time = timeit.timeit("sum(list_data)", setup=setup_code, number=10)
# Zeit für das Array messen
array_time = timeit.timeit("sum(array_data)", setup=setup_code, number=10)
print(f"Zeit zum Summieren von {num_elements} Elementen (10 Mal):")
print(f"list: {list_time:.4f} Sekunden")
print(f"array: {array_time:.4f} Sekunden")
Beispiel für die Verwendung
if name == „main„:
compare_performance()
**Ausgabe:**
Zeit zum Summieren von 10000000 Elementen (10 Mal):
list: 2.1106 Sekunden
array: 1.1549 Sekunden
---
### 3. Direkte Arbeit mit C-Bibliotheken (`ctypes`, `struct`)
`array.array` ist ideal für die Übergabe von Daten an Low-Level-Bibliotheken, die in C geschrieben sind, da seine interne Struktur mit C-Arrays kompatibel ist.
#### Beispiel mit `ctypes`:
python
import array
from ctypes import c_double, CDLL
def demonstrate_ctypes_usage() -> None:
„““
Demonstriert die Übergabe von array.array an eine C-Funktion über ctypes.
„““
# Array mit Double-Precision-Zahlen (Typ ‚d‘)
py_array = array.array(‚d‘, [1.1, 2.2, 3.3, 4.4])
# Erstellen eines C-kompatiblen Arrays aus py_array
# Die Funktion (c_double * len(py_array)) erstellt einen Typ "Array von 4 c_double"
# (*py_array) entpackt das Python-Array in die Argumente dieses Konstruktors
c_array = (c_double * len(py_array))(*py_array)
# Hier könnte ein Aufruf an eine C-Funktion stehen, zum Beispiel:
# my_c_library = CDLL("./libmath.so")
# my_c_library.sum_doubles(c_array, len(c_array))
print(f"Python-Array: {py_array}")
print(f"C-kompatibles Array (ctypes): {[val for val in c_array]}")
Beispiel für die Verwendung
if name == „main„:
demonstrate_ctypes_usage()
#### Beispiel mit `struct` zur Datenpaketierung:
python
import array
import struct
def demonstrate_struct_packing(data: list[int]) -> bytes:
„““
Packt ein Array von Ganzzahlen in einen binären String.
Args:
data (list[int]): Liste der zu packenden Ganzzahlen.
Returns:
bytes: Binäre Darstellung der Daten.
"""
arr = array.array('i', data)
# Erstellen eines Format-Strings wie '3i' für 3 Ganzzahlen
format_string = f'{len(arr)}i'
# Daten in binäres Format packen
binary_data = struct.pack(format_string, *arr)
print(f"Ursprüngliches Array: {arr}")
print(f"Binäre Daten: {binary_data}")
# Überprüfung: wieder entpacken
unpacked_data = struct.unpack(format_string, binary_data)
print(f"Entpackte Daten: {unpacked_data}")
return binary_data
Beispiel für die Verwendung
if name == „main„:
demonstrate_struct_packing([10, 20, 30])
---
### 4. Effiziente Serialisierung und Deserialisierung
Die Methoden `.tobytes()` und `.frombytes()` ermöglichen die schnelle Umwandlung eines Arrays in Bytes und zurück, was ideal zum Speichern in Dateien oder zur Übertragung über ein Netzwerk ist.
python
import array
def handle_binary_data() -> None:
„““
Demonstriert die Serialisierung und Deserialisierung von array.array in Bytes.
„““
# Ursprüngliches Array erstellen
source_array = array.array(‚i‘, [1, 2, 3, 4, 5])
print(f“Ursprüngliches Array: {source_array}“)
# Array in Bytes serialisieren
binary_data = source_array.tobytes()
print(f"Daten in Bytes: {binary_data}")
# Aus Bytes in ein neues Array deserialisieren
new_array = array.array('i')
new_array.frombytes(binary_data)
print(f"Wiederhergestelltes Array: {new_array}")
# Integrität prüfen
assert source_array == new_array, "Daten stimmen nicht überein!"
print("Datenintegrität bestätigt.")
Beispiel für die Verwendung
if name == „main„:
handle_binary_data()
---
### 5. Garantie der Typ-Homogenität
`array.array` erzwingt strikt nur einen Datentyp, der bei der Erstellung angegeben wird. Dies verhindert das versehentliche Hinzufügen von Elementen eines anderen Typs.
python
import array
def demonstrate_type_safety() -> None:
„““
Zeigt, dass array.array das Hinzufügen von Elementen eines anderen Typs nicht erlaubt.
„““
arr = array.array(‚i‘, [100, 200, 300])
print(f“Ganzzahl-Array: {arr}“)
try:
# Versuch, ein String-Element hinzuzufügen
arr.append('hello')
except TypeError as e:
# Erwartete Ausnahme
print(f"\nDer Versuch, 'hello' hinzuzufügen, hat einen Fehler ausgelöst: {e}")
print("Dies bestätigt die strikte Typisierung des Arrays.")
Beispiel für die Verwendung
if name == „main„:
demonstrate_type_safety()
---
### 6. Direktes Schreiben und Lesen aus Binärdateien
Die Methoden `.tofile()` und `.fromfile()` vereinfachen die Arbeit mit Binärdateien und vermeiden eine zwischengeschaltete Serialisierung.
python
import array
from pathlib import Path
def work_with_binary_files(file_path_str: str = „data.bin“) -> None:
„““
Schreibt ein Array in eine Binärdatei und liest es wieder ein.
Args:
file_path_str (str, optional): Dateiname zum Speichern.
Standardmäßig "data.bin".
"""
file_path = Path(file_path_str)
source_array = array.array('f', [1.5, 2.7, 3.14])
try:
# In Datei schreiben
with file_path.open('wb') as f:
source_array.tofile(f)
print(f"Array {source_array} wurde in die Datei '{file_path}' geschrieben.")
# Aus Datei lesen
new_array = array.array('f')
with file_path.open('rb') as f:
# 3 Elemente vom Typ 'f' (float) lesen
new_array.fromfile(f, len(source_array))
print(f"Array {new_array} wurde aus der Datei gelesen.")
assert source_array == new_array
finally:
# Garantiertes Löschen der Datei nach der Ausführung
if file_path.exists():
file_path.unlink()
print(f"Temporäre Datei '{file_path}' wurde gelöscht.")
Beispiel für die Verwendung
if name == „main„:
work_with_binary_files()
„`
🔹 Vergleichstabelle: array.array vs. list
| Merkmal | array.array | list |
|---|---|---|
| Datentyp | Homogene Primitive (Zahlen, Zeichen) | Beliebige Python-Objekte |
| Speicher | Geringer Verbrauch | Hoher Verbrauch |
| Leistung | Hoch bei numerischen Operationen | Geringer bei numerischen Operationen |
| API | Begrenzter Satz von Methoden | Reichhaltige und flexible API |
| C-Kompatibilität | Hoch, direkte Datenübertragung | Konvertierungen erforderlich |
| Binäre Serialisierung | Integrierte Methoden (.tobytes, .tofile) | Erfordert struct, pickle usw. |
Fazit:
🚀 Verwenden Sie array.array, wenn Sie mit großen Mengen homogener numerischer Daten arbeiten und wenn Leistung und effiziente Speichernutzung für Sie entscheidend sind.
Für die meisten alltäglichen Aufgaben, bei denen Flexibilität und die Speicherung heterogener Daten erforderlich sind, bleibt list die beste Wahl.