Zum Inhalt springen

📦 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

Merkmalarray.arraylist
DatentypHomogene Primitive (Zahlen, Zeichen)Beliebige Python-Objekte
SpeicherGeringer VerbrauchHoher Verbrauch
LeistungHoch bei numerischen OperationenGeringer bei numerischen Operationen
APIBegrenzter Satz von MethodenReichhaltige und flexible API
C-KompatibilitätHoch, direkte DatenübertragungKonvertierungen erforderlich
Binäre SerialisierungIntegrierte 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.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert