Listen in Python

Listen gehören zu den zentralen Objekttypen in Python und werden aufgrund ihrer Flexibilität und Performance gerne dazu verwendet, komplexe Informationen abzulegen. In diesem Beitrag erfahren Sie, wie mit Listen in Python umzugehen ist.

Inhalt

Eine Liste ist eine Verkettung von Daten- bzw. Objekttypen und wird auch manchmal als Container bezeichnet: In sie passt nunmal Allerhand hinein! Sie kann dabei sowohl homogener als auch heterogener Natur sein. Um eine homogene Liste handelt es sich, wenn sie build-in Daten nur eines Typs enthält. Verkettet eine Liste hingegen unterschiedliche Daten- oder Objekttypen bezeichnen wir sie als heterogen. Listen können dabei sehr komplexe Formen annehmen, bspw. wenn man Listen ineinander verschachtelt. Von Verschachtelung spricht man, wenn Listen wiederum Listen enthalten.

Erstellen von Listen

Eine Liste in Python wird durch rechteckige Klammern erstellt. Die Elemente der Liste werden mit einem Komma voneinander getrennt.

meineListe = [] # Erstellt eine leere Liste
meineIntegerListe = [1,2,3,4,5,6,7] # Erstellt homogene Liste mit Integer-Werten
meineStringListe  = ['A','B','C']   # Erstellt homogene Liste mit Strings
meineContainerListe = ["Hallo", [1,2,3], 5, "A"] # Erstellt eine heterogene Liste

print(meineIntegerListe)

List Comprehensions

List-Comprehensions bieten eine elegante Form, um über Listen zu iterieren. Sie sind nichts anderes als eine Kurzschreibweise der for-Schleife. Ihre Syntax besteht aus einer Expression auf die die Schleifenanweisung folgt. Eingerahmt wird der Ausdruck der List-Comprehension durch eckige Klammern. Über eine if-Bedingung nach der for-Anweisung können Daten aus der Liste gefiltert werden (siehe Indizieren von Listen). Sollen Daten der Liste unterschiedlich Behandelt werden können auch if-else-Anweisungen in eine List-Comprehension eingebaut werden. Diese stehen allerdings, anders als die if-Bedingung, noch vor der for-Anweisung und sind streng genommen nicht syntaktischer Teil der List-Comprehension, sondern gehören als sog. Conditional Expression zur Expression selbst.

meineListe = [1,2,3,4,5]

Quadieren der Werte in der Liste mit for-Schleife:

result = []
for i in meineListe:
    result.append(i*i)

Gleiches Ergebnis wie mit der for-Schleife über List-Comprehension.

result = [i*i for i in meineListe] # [1, 4, 9, 16, 25]

If-Anweisungen in List-Comprehensions werden an das Ende des gesamten Ausdrucks angehangen.

result = [i*i for i in meineListe if i <= 3] # [1, 4, 9]

If-Else Anweisungen stehen direkt nach der Expression und vor dem Schleifenausdruck.

result = [i*i if i <= 3 else i*100 for i in meineListe] # [1, 4, 9, 400, 500]

Indizieren von Listen

Innerhalb des Datenmanagements im Vorfeld von Analysen ist eine häufige Tätigkeit das Indizieren von Daten. Indizieren bedeutet,
dass man aus einer Datenmenge bestimmte Daten anspricht. Grundsätzlich lassen sich 3 Techniken voneinander
Unterscheiden:

  1. Indizieren der Daten aufgrund von Positionen. Aus Analystensicht stellt sich dabei die Frage: Welcher Wert verbirgt sich hinter einer bestimmten Position? Python bietet für diese Form ein spezielle aber in der Praxis häufig zu gebrauchende Indizierungsmöglichkeit Folgen.
  2. Indizieren der Daten aufgrund von Filtern – also logischen Bedingungen. Hier lautet die Frage aus Analystensicht: Welche Positionen verbergen sich hinter bestimmten Werten?
  3. Eine häufige Anwendung besteht außerdem darin, einen logischen Wert für jedes Element der Liste zu erzeugen, wenn eine Bedingung zutrifft. Das Ergebnis ist eine homogene Liste boolscher Werte, die in Funktionen oder komplexen Filteranweisungen als Maske dienen kann. Diese spezielle Form der Indizierung werden wir ebenfalls im Beispielcode angehen.
meineListe = [5,7,9,8,1,2,1,1,4,7]

1. Indizieren mit Positionen

meineListe[:]    # Welche Werte befinden sich hinter der Liste? 
meineListe[:2]   # Welche Werte befinden sich hinter den ersten 2 Positionen der Liste?
meineListe[3]    # Welcher Wert befindet sich an Position 3?
meineListe[3:5]  # Welche Werte befinden sich an den Positionen 3 bis 5?
meineListe[-1]   # Welcher Wert befindet sich an der letzten Position der Liste?

# Liegt eine Menge als Indizes vor, mit der aus der Liste eine Untermenge gezogen werden soll, kann man dies über die List-Comprehension in einer Zeile ausführen:
indexMenge = [1,3,6,7]
[meineListe[i] for i in indexMenge]
>>[7, 8, 1, 1]
Indizieren mit Postion über Folgen
meineListe[::2]  # Welche Werte befinden sich hinter jeder 2ten Position der Liste? 
meineListe[::4]  # Welche Werte befinden sich hinter jeder 2ten Position der Liste?

2. Indizieren mit Filtern

[i for i in meineListe if i > 6]
[i for i in meineListe if (i > 3) and (i < 7)]

3. Erzeugen eines logischen Vektors über die länge der Liste.

[True if i==1 else False for i in meineListe]

Listen im Speicher

Listen zählen zu den Mutable Objects in Python. Wenn sich die Liste ändert, bleibt ihr physikalischer Speicherort bestehen und es wird intern keine Kopie der Liste erstellt. Will man eine Kopie einer Liste erzeugen, muss dies explizit über die Methode copy geschehen (siehe hierzu: Die Methoden einer Liste).

Listen selbst sind sehr Speichereffizient implementiert. Sie benötigen nur äußerst wenig Speicherplatz da sie nicht die eigentlichen Objekte, sondern die Referenzen auf die Objekte enthalten. Der Speicherbedarf einer Liste hängt damit einzig und allein von der Anzahl der Elemente in der Liste ab, nicht von der Größe der Elemente.

An dem Beispielcode unten sehen wir, wie das Speichermanagement in Listen funktioniert. Eine Leere Liste hat eine Größe von 64 Bytes. Jedes weitere Element der Liste braucht konstant weitere 8 Bytes Speicher, auch wenn das eigentliche Objekt auf den das Listenelement referenziert, anderer Größe ist.

import sys 

x = []
print(sys.getsizeof(x))
>> 64 # Größe einer leeren Liste.

x = ["Hallo",[1,2,3],"A"]
print(sys.getsizeof(x))
>> 88 # Größe einer Liste mit 3 Elementen.

print(sys.getsizeof(x[0]), sys.getsizeof(x[1]), sys.getsizeof(x[2]))
>> 54 88 50 # Größe der einzelnen Elemente.

Die Methoden einer Liste

Die wichtigsten Methoden von Listen im Überblick:

Methode

Beispiel

Funktionsweise

append x = [1,2,3]
x.append(4)
print(x)
>>[1, 2, 3, 4]
Hängt der Liste ein Element an.
clear x = [1,2,3]
x.clear()
print(x)
>>[]
Löscht alle Elemente der Liste.
copy x = [1,2,3]
y = x
print(id(x))
>>1269477764296
print(id(y))
>>1269477764296

y = x.copy()
print(id(x))
>>1269477764296
print(id(y))
>>1269477514440

Erstellt eine Kopie der Liste im Speicher.
count x = [7,3,2,7,4,5,5,3,1,5,7]
x.count(7)
>>3
Zählt das Item in der Liste.
extend x = [1,2,3]
y = [4,5,6,7]
x.extend(y)
print(x)
>>[1,2,3,4,5,6,7]
Hängt der Liste eine andere Liste an.
index x = [7,3,2,7,4,5,5,3,1,5,7]
x.index(3)
>>1
Gibt die erste Position des Items in der Liste zurück.
insert x = [1,2,4]
x.insert(2,3)
print(x)
>>[1,2,3,4]
Injeziert an eine Position ein Item.
pop x = [1,2,3]
x.pop()
print(x)
>>[1,2]
Entfernt das Letzte Item von der Liste.
remove x = [1,2,3]
x.remove(2)
print(x)
>>[1,3]
Entfernt alle Items aus der Liste, die dem Parameter entsprechen.
reverse x = [3,2,1]
x.reverse()
print(x)
>>[1,2,3]
Dreht die Sortierung der Liste um.
sort x = [4,5,2,1,3]
print(x)
x.sort()
>>[1,2,3,4,5]
Sortiert die Liste.

Weitere Funktionen auf Listen

meineListe = [1,2,3]

len() – Die Länge der Liste ermitteln

len(meineListe)

Arithmetische Operationen auf Listen

max(meineListe)
min(meineListe)

# Das Modul statistics verfügt über zahlreiche Funktionen aus der Deskriptiven Statistik 
from statistics import mean, median, stdev, variance
mean(meineListe)
median(meineListe)
stdev(meineListe)
variance(meineListe)

zip – Ein Reisverschluss für Listen

x = [1,2,3]
y = ['A','B','C']
z = zip(x,y)

for i in z:
    print(i)
>> (1, 'A')
>> (2, 'B')
>> (3, 'C')

unzipped = zip(*z)
for i in unzipped:
    print(i)
>> (1, 2, 3)
>> ('A', 'B', 'C')

Wertezuweisung auf Listen

Mit den hier vorgestellen Techniken und Funktionen lassen sich an Listen Wertzuweisungen vornehmen.

meineListe = [5,7,9,8,1,2,1,1,4,7]

meineListe[2] = 100 # Zuweisung an die Position 2
>>[5, 7, 100, 8, 1, 2, 1, 1, 4, 7]

meineListe[:2] = [100,200,300] # Zuweisung an die Positionen 0 bis 2
>>[100, 200, 300, 9, 8, 1, 2, 1, 1, 4, 7]

Ist der Index größer als die Anzahl der zugewiesenen Werte, verkürzt sich die Liste. Hier werden die 3 Werte 100,200 und 300 den 4 Positionen 0,1,2,3 zugewiesen.

meineListe[:4] = [100,200,300] # Zuweisung an die Positionen 0 bis 3
>>[100, 200, 300, 1, 2, 1, 1, 4, 7]

Ist der Index kleiner als die Anzahl der zugewiesenen Werte, vergrößert sich die Liste. Hier werden die 3 Werte 100,200 und 300 den 2 Positionen 0,1 zugewiesen.

meineListe[:2] = [100,200,300] # Zuweisung an die Positionen 0 bis 1
>>[100, 200, 300, 9, 8, 1, 2, 1, 1, 4, 7]

Zuweisungen auf beliebigen Indizes

Sollen Zuweisungen über einen nicht-zusammenhängenden Index erfolgen, existieren verschiedene herangehensweisen. Hier werden 3 Methoden vorgestellt:

index = [3,5,9]
newValues = [1000,2000,3000]

Methode 1: Über Tupel aus Index und Werten iterieren

for i in zip(index,newValues):
    meineListe[i[0]] = i[1]
print(meineListe)

meineListe = [5,7,9,8,1,2,1,1,4,7]
for ind, val in zip(index,newValues):
    meineListe[ind] = val

>>[5, 7, 9, 1000, 1, 2000, 1, 1, 4, 3000]

Methode 2: Liste in array konvertieren und indexbasierte Zuweisung vornehmen

meineListe = [5,7,9,8,1,2,1,1,4,7]
import numpy as np
meinArray = np.array(meineListe)
meinArray[index] = newValues
meineListe = list(meinArray)
>>[5, 7, 9, 1000, 1, 2000, 1, 1, 4, 3000]

Methode 3: Mit der Build-In Funktion enumerate über die Liste iterieren

meineListe = [5,7,9,8,1,2,1,1,4,7]
for ind, value in enumerate(index):
    meineListe[value] = newValues[ind]
>>[5, 7, 9, 1000, 1, 2000, 1, 1, 4, 3000]

Zur Performance von Listen

  • In Python ist das Erstellen und anschließende Befüllen von Listen nicht Performancekritisch. Einer leeren Liste wird bei ihrer Initialisierung im Speicher ein Puffer für eine Vielzahl von Elementen eingeräumt. Wenn dieser Puffer zu Ende geht, wird wiederum Speicher für eine Vielzahl von Elementen freigeräumt. Durch dieses Vorgehen wird nicht bei jedem neuen Element der Liste Speicherkapazität allokiert.
  • Die Geschwindigkeit mit der Elemente in die Liste insertet werden, hängt davon ab, wie viele Items rechts der Position stehen, auf die das Item rückt. Das Anfügen eines Items an das Ende der Liste ist dementsprechend schnell, je weiter vorne Eingefügt wird, desto länger dauert der Prozess.
  • Die Performance für das Löschen eines Items verhält sich wie beim Einfügen eines Items. Je weiter vorne in der Liste ein Element gelöscht werden soll, desto länger dauert dies.
  • Die Zeit die Liste in ihrer Sortierung umzudrehen (siehe Abschnitt: Methoden von Listen) ist proportional zu ihrer Größe.
  • Die Performance für die Neu-Ordnung einer Liste ist abhängig von dem Objekttyp (oder den Objekttypen), den die Liste enthält, ihrer Größe und der Vorschrift, nach der die Sortierung erfolgen soll.