var1 = 'Text'
var2 = var1
var2 = var2.lower()
print(var1)Text
print(var2)text
Woche 2
9. Oktober 2025
Wie bereits erwähnt sind Listen die am häufigsten verwendete Art von Sammlungen in Python. Sie werden mit eckigen Klammern geschrieben, können heterogen sein und auch verschachtelt werden.
Außerdem sind Listen geordnert, können Duplikate enthalten und sie sind mutable. Was diese Begriffe bedeuten, wird im Folgenden genauer erläutert. Da Listen die erste Art Sammlung ist, die wir uns genauer ansehen, werden wir ausführlicher auf die Eigenschaften und Funktionen von Listen eingehen um dann bei den anderen Sammlungen nur noch auf die Unterschiede zu Listen eingehen zu müssen.
’| Eigenschaft | Liste | Tuple | Set | Dictionary |
|---|---|---|---|---|
| Darstellung | [ ] | ( ) | { } | { } |
| Beispiel | [1, 2, 2, 'drei'] | (1, 2, 2, 'drei') | {'drei', 1, 2} | {1: 'a', 2: 'b', 3: 'b', 'drei': 'c'} |
| Homogenität | Nicht-homogen | Nicht-homogen | Nicht-homogen | Nicht-homogen |
| Verschachtelung möglich | Ja | Ja | Ja | Ja |
| Geordnet | Ja | Ja | Nein | Ja (Python 3.7+) |
| Duplikate erlaubt | Ja | Ja | Nein | Nein (nur Werte) |
| Mutability | {{< fa chalkboard >}} Mutable | {{< fa print >}} Immutable | {{< fa chalkboard >}} Mutable | {{< fa chalkboard >}} Mutable |
| Erstellung aus x | list(x) | tuple(x) | set(x) | dict(x) |
| Erstellung leer | [] | () | set() | {} |
’
Bei der Zuweisung von Variablen in Python muss man sich bewusst sein, dass es “mutable” (veränderbar) und “immutable” (unveränderbare) Objekttypen gibt.
Der wichtige Unterschied soll durch diese Beispiele verdeutlicht werden. In beiden Fällen erzeugen wir zunächst eine Variable var1 und weisen ihr einen Wert zu. Dann weisen wir var1 einer weiteren Variable var2 zu und verändern (vermeintlich nur) var2.
Immutable
Alle grundlegenden Datentypen sind immutable, sodass bei der Zuweisung des Strings 'Text' zu var1 ein immutable Objekt erzeugt wird. Durch die Zuweisung von var1 zu var2 entsteht zunächst lediglich ein Verweis auf dasselbe Objekt. Sobald allerdings var2 mit der Funktion lower() verändert wird (Großbuchstaben werden zu Kleinbuchstaben), wird ein neues Objekt var2 im Speicher erzeugt, sodass var2, nicht aber var1 verändert erscheinen.
Als Eselsbrücke kann man sich vorstellen, dass die Informationen auf einem ausgedruckten Blatt Papier stehen. Sie sind somit unveränderbar und können nur durch ein neues Blatt Papier ersetzt werden.
Beispiele:
Mutable
In var1 wird hier eine Liste mit den Zahlen 1, 2 und 3 erzeugt. Mit der Zuweisung von var1 zu var2 entsteht auch hier zunächst ein Verweis auf dieselbe Liste. Schließlich wird var2 mit der Funktion append() um die Zahl 4 erweitert. Da Listen mutable Objekte sind, wird das Objekt im Speicher verändert. Da var2 lediglich auf var1 verweist, wird die zugrundeliegende Liste in var1 geändert, sodass letztendliche var1 und var2 verändert erscheinen.
Als Eselsbrücke kann man sich vorstellen, dass die Informationen auf einem Whiteboard stehen. Sie sind somit veränderbar und können durch Überschreiben oder Löschen verändert werden.
Beispiele:
Je nach Erfahrung z.B. mit anderen Programmiersprachen fühlt sich die eine oder andere Variante ggf. intuitiver an. Wie so oft haben beide ihre Vor- und Nachteile.
Der Vorteil von immutable Objekten ist, dass sie die Integrität und Vorhersehbarkeit des Codes erhöhen. Da ein immutable Objekt nach seiner Erstellung nicht mehr verändert werden kann, verringert dies das Risiko unbeabsichtigter Nebeneffekte. Das macht den Code zuverlässiger und oft einfacher zu debuggen, da die Werte von immutablen Objekten vorhersagbar bleiben.
Der Vorteil von mutable Objekten ist hingegen ihre Effizienz, da nicht bei jeder Änderung eine neue Kopie des Objekts erzeugt werden muss. In Performance-kritischen Anwendungen bzw. mit großen Datenmengen kann dies einen erheblichen Unterschied machen.
Insgesamt ist die Wahl zwischen “mutable” und “immutable” Objekten eine Frage des spezifischen Anwendungsfalls und des gewünschten Verhaltens.
Eine Liste wird erstellt, indem eine durch Kommas getrennte Sequenz von Objekten in eckigen Klammern angegeben wird:
Wie bereits erwähnt, können auch mehrere Listen (oder andere Sammlungen) in eine Liste getan werden, sodass eine geschachtelte Liste (nested list) entsteht:
[['Etwas', 42, 'Text', 42, 12.6, True], [1, 6, 3, 2]]
Es ist auch möglich eine leere Liste via [] zu erzeugen, sowie bestimmte andere Objekte mit der Funktion list() in Listen umzuwandeln.
Listen und Sequenzen in Python sind indiziert, d.h. jede Position in der Sequenz hat eine zugehörige Nummer - den Index. Mit diesem kann man auf den Wert an dieser Position zugreifen. Python-Sequenzen beginnen bei Null (!), sodass das erste Element einer Sequenz den Index 0 hat, das zweite Element den Index 1 und so weiter. Ein Element aus einer Liste kann abgerufen werden, indem man den Index in eckige Klammern hinter den Listennamen setzt. Es kann sogar mit negative Indices rückwärts auf die Liste zugegriffen werden, dann ist das letzte Element aber bei -1:
Mittels Indizierung kann man die einzelnen Elemente nicht nur ansprechen, sondern auch verändern, wobei del() genutzt wird um ein Element zu löschen:
Auch die Elemente in geschachtelten Listen können entsprechend mit zusätzlichen Indizes angesprochen werden:
Slicing, also das Ausschneiden einer Teilmenge, funktioniert ähnlich wie die Indizierung, allerdings kann in den eckigen Klammern die Syntax [start:stop:step] angewendet werden. Dabei bezeichnet
start den Index des ersten Elements ab und inklusive dem ausgeschnitten werden soll (Default = 0)stop den Index des letzten Elements bis und exklusive (!) dem ausgeschnitten werden soll (Default = letztes Element)step wie of im Ausschnitt Elemente abgetasted werden sollen (Default = 1). Auch hier kann mit negativen Werten gearbeitet um rückwärts abzutasten.Lässt man die Werte frei, werden die defaults angewendet:
Mit list.append() kann ein Element ans Ende einer Liste hinzugefügt werden, während mit list.insert() ein Element an einer bestimmten Position (=Index; siehe weiter unten) eingefügt werden kann.
Wenn nicht nur ein einzelnes Element, sondern z.B. eine andere Liste zu einer bestehenden Liste hinzugefügt werden soll, so funktioniert dies entweder schlicht mit einem + oder mit list.extend(). Der Unterschied ist, dass + eine neue Liste erzeugt, während list.extend() die bestehende Liste verändert. Wir demonstrieren dies mit Liste b von oben:
Der Befehle list.clear entfernt alle Elemente aus einer Liste, während list.remove() (nur!) das erste passende Element aus einer Liste entfernt. Um ein Element an einer bestimmten Position zu entfernen, kann del() in Kombination mit dem Index (nicht dem zu entfernenden Wert!) verwendet werden.
Schließlich sei noch die Funktion list.pop() erwähnt, die stets das letzte Element einer Liste herauslöst, d.h. in der ursprünglichen Liste entfernt und gleichzeitig zurückgibt:
Weitere gebräuchliche Listenfunktionen sind list.sort(), welche die Elemente einer Liste sortiert, und list.reverse(), welche die Reihenfolge der Elemente umkehrt. Beide Funktionen verändern die Liste, auf die sie angewendet werden. list.sort() kann auch mit dem Schlüsselwort reverse = True angewendet werden, um die Liste in umgekehrter Reihenfolge zu sortieren. Wir nutzen hier die zahlen_liste = [1, 6, 3, 2] von oben.
Es ist oft hilfreich bestimmte Attribute von Listen zu extrahieren bzw. zu prüfen. Die Länge einer Liste, also die Anzahl der in ihr enthaltenen Elemente, kann mittels len() ausgegeben werden und wird auch häufig für weitere Operationen benötigt. Ebenso können die Schlüsselwörter in und not verwendet werden um zu prüfen ob ein Element bzw. ob ein Element nicht in einer Liste enthalten ist. Schließlich kann auch mittels list.count() gezählt werden wie oft ein Element in einer Liste enthalten ist.
Gegeben, dass die Liste ausschließlich Zahlen enthält, können auch einfache Rechenoperationen wie Bestimmung des Maximums, Minimums oder der Summe durchgeführt werden.
Wie bereits erwähnt, sind Listen in Python mutable Objekte. Natürlich kann es aber auch gewünscht sein eine Kopie einer Liste zu erzeugen, die eben nicht dauerhaft auf eine andere Liste verweist, sondern ab dem Moment eine echte, unabhängige Kopie darstellt, mit der separat weitergearbeitet werden kann. Zumindest bedingt (!) ist dies möglich via list.copy():
Wie zu sehen wird - im Gegensatz zum Beispiel mit liste1 und liste2 - das Objekt liste3 eben nicht durch das Anwenden der append() Funktion auf liste4 verändert. Wie zu erwarten verhält sich liste4 also als unabhängige Kopie von liste3. Dies stimmt leider wie gesagt nur bedingt, da list.copy() eine sogenannte flache (shallow) Kopie erzeugt. Dies meint, dass das Erzeugen von unabhängigen Kopien nur auf der obersten Listen-Ebene erfolgt. Sobald also eine Liste geschachtelt ist und auf den unteren Ebenen weitere mutable Objekte enthält, sind diese weiterhin nicht unabhängig. Um auch diese unabhängig zu machen, also eine tiefe (deep) Kopie zu erzeugen, kann die Funktion copy.deepcopy() aus dem copy-Modul verwendet werden. Es folgen drei Beispiele, die dies verdeutlichen sollen. Die Beispiele mit listeA bis listeD entsprechen dabei der Vorgehensweise von oben, wobei mit listeF die tiefe Kopie von listeE erzeugt wird. In allen Fällen wird sowohl ein Element in der obersten Listenebene, als auch in der Listenebene darunter verändert, sodass sich die Ergebnisse aller drei Beispiele unterscheiden:
---
title: Listen
author: "Woche 2"
---
Wie bereits erwähnt sind Listen die am häufigsten verwendete Art von Sammlungen in Python. Sie werden mit eckigen Klammern geschrieben, können heterogen sein und auch verschachtelt werden.
Außerdem sind Listen geordnert, können Duplikate enthalten und sie sind mutable. Was diese Begriffe bedeuten, wird im Folgenden genauer erläutert. Da Listen die erste Art Sammlung ist, die wir uns genauer ansehen, werden wir ausführlicher auf die Eigenschaften und Funktionen von Listen eingehen um dann bei den anderen Sammlungen nur noch auf die Unterschiede zu Listen eingehen zu müssen.
```{python}
#| echo: false
from utils import *
```
::: {.content-visible when-format="html"}
```{python}
#| echo: false
#| results: asis
datstr.to_html(index=False)
```
:::
::: {.content-visible when-format="pdf"}
```{python}
#| echo: false
#| results: asis
datstr
```
:::
# Mutable vs. Immutable
Bei der Zuweisung von Variablen in Python muss man sich bewusst sein, dass es "mutable" (veränderbar) und "immutable" (unveränderbare) Objekttypen gibt.
- Objekte, die **immutable** sind, können nach ihrer Erzeugung nicht verändert werden. Führt man eine Operation aus, die das Objekt zu verändern scheint, so wird im Speicher tatsächlich ein völlig neues Objekt erzeugt.
- Objekte, die **mutable** sind, werden stattdessen bei einer entsprechenden Operation direkt im Speicher verändert. Wenn sich eine andere Variable auf dasselbe Objekt bezieht, so ändert sich auch der Wert dieser Variable.
Der wichtige Unterschied soll durch diese Beispiele verdeutlicht werden. In beiden Fällen erzeugen wir zunächst eine Variable `var1` und weisen ihr einen Wert zu. Dann weisen wir `var1` einer weiteren Variable `var2` zu und verändern (vermeintlich nur) `var2`.
:::: {.columns}
::: {.column width='45%'}
**Immutable**
```{python}
var1 = 'Text'
var2 = var1
var2 = var2.lower()
print(var1)
print(var2)
```
Alle grundlegenden Datentypen sind *immutable*, sodass bei der Zuweisung des Strings `'Text'` zu `var1` ein *immutable* Objekt erzeugt wird. Durch die Zuweisung von `var1` zu `var2` entsteht zunächst lediglich ein Verweis auf dasselbe Objekt. Sobald allerdings `var2` mit der Funktion `lower()` verändert wird (Großbuchstaben werden zu Kleinbuchstaben), wird ein neues Objekt `var2` im Speicher erzeugt, sodass `var2`, nicht aber `var1` verändert erscheinen.
{{< fa print >}} Als Eselsbrücke kann man sich vorstellen, dass die Informationen auf einem ausgedruckten Blatt Papier stehen. Sie sind somit unveränderbar und können nur durch ein neues Blatt Papier ersetzt werden.
Beispiele:
+ Zahlen
+ Strings
+ Tuples
:::
::: {.column width='10%'}
:::
::: {.column width='45%'}
**Mutable**
```{python}
var1 = [1, 2, 3]
var2 = var1
var2.append(4)
print(var1)
print(var2)
```
In `var1` wird hier eine Liste mit den Zahlen 1, 2 und 3 erzeugt. Mit der Zuweisung von `var1` zu `var2` entsteht auch hier zunächst ein Verweis auf dieselbe Liste. Schließlich wird `var2` mit der Funktion `append()` um die Zahl 4 erweitert. Da Listen *mutable* Objekte sind, wird das Objekt im Speicher verändert. Da `var2` lediglich auf `var1` verweist, wird die zugrundeliegende Liste in `var1` geändert, sodass letztendliche `var1` und `var2` verändert erscheinen.
{{< fa chalkboard >}} Als Eselsbrücke kann man sich vorstellen, dass die Informationen auf einem Whiteboard stehen. Sie sind somit veränderbar und können durch Überschreiben oder Löschen verändert werden.
Beispiele:
+ Listen
+ Dictionaries
+ Sets
:::
::::
Je nach Erfahrung z.B. mit anderen Programmiersprachen fühlt sich die eine oder andere Variante ggf. intuitiver an. Wie so oft haben beide ihre Vor- und Nachteile.
:::: {.columns}
::: {.column width='45%'}
Der Vorteil von immutable Objekten ist, dass sie die Integrität und Vorhersehbarkeit des Codes erhöhen. Da ein immutable Objekt nach seiner Erstellung nicht mehr verändert werden kann, verringert dies das Risiko unbeabsichtigter Nebeneffekte. Das macht den Code zuverlässiger und oft einfacher zu debuggen, da die Werte von immutablen Objekten vorhersagbar bleiben.
:::
::: {.column width='10%'}
:::
::: {.column width='45%'}
Der Vorteil von mutable Objekten ist hingegen ihre Effizienz, da nicht bei jeder Änderung eine neue Kopie des Objekts erzeugt werden muss. In Performance-kritischen Anwendungen bzw. mit großen Datenmengen kann dies einen erheblichen Unterschied machen.
:::
::::
Insgesamt ist die Wahl zwischen "mutable" und "immutable" Objekten eine Frage des spezifischen Anwendungsfalls und des gewünschten Verhaltens.
# Mit Listen arbeiten
## Erstellen
Eine Liste wird erstellt, indem eine durch Kommas getrennte Sequenz von Objekten in eckigen Klammern angegeben wird:
:::: {.grid}
::: {.g-col-6}
```{python}
meine_liste = ['Etwas', 42, 'Text', 42, 12.6, True]
meine_liste
```
:::
::: {.g-col-6}
```{python}
zahlen_liste = [1, 6, 3, 2]
zahlen_liste
```
:::
::::
Wie bereits erwähnt, können auch mehrere Listen (oder andere Sammlungen) in eine Liste getan werden, sodass eine geschachtelte Liste (*nested list*) entsteht:
```{python}
geschachtelte_liste = [meine_liste, zahlen_liste]
geschachtelte_liste
```
Es ist auch möglich eine leere Liste via `[]` zu erzeugen, sowie bestimmte andere Objekte mit der Funktion `list()` in Listen umzuwandeln.
:::: {.grid}
::: {.g-col-6}
```{python}
x = []
x
```
:::
::: {.g-col-6}
```{python}
x = list('Gut')
x
```
:::
::::
## Indizierung
Listen und Sequenzen in Python sind indiziert, d.h. jede Position in der Sequenz hat eine zugehörige Nummer - den Index. Mit diesem kann man auf den Wert an dieser Position zugreifen. Python-Sequenzen beginnen bei Null **(!)**, sodass das erste Element einer Sequenz den Index 0 hat, das zweite Element den Index 1 und so weiter. Ein Element aus einer Liste kann abgerufen werden, indem man den Index in eckige Klammern hinter den Listennamen setzt. Es kann sogar mit negative Indices rückwärts auf die Liste zugegriffen werden, dann ist das letzte Element aber bei -1:
```{python}
x = ['Timon', 'sagt', 'Hakuna', 'Matata', '!']
```
:::: {.grid}
::: {.g-col-4}
```{python}
x[0]
```
:::
::: {.g-col-4}
```{python}
x[2]
```
:::
::: {.g-col-4}
```{python}
x[-1]
```
:::
::::
Mittels Indizierung kann man die einzelnen Elemente nicht nur ansprechen, sondern auch verändern, wobei `del()` genutzt wird um ein Element zu löschen:
```{python}
x[0] = 'Pumbaa'
x
```
Auch die Elemente in geschachtelten Listen können entsprechend mit zusätzlichen Indizes angesprochen werden:
:::: {.grid}
::: {.g-col-6}
```{python}
x = [['D', 'B', 'Z'], [1, 2]]
x[0]
```
:::
::: {.g-col-6}
```{python}
x = [['D', 'B', 'Z'], [1, 2]]
x[0][1]
```
:::
::::
## Slicing
*Slicing*, also das Ausschneiden einer Teilmenge, funktioniert ähnlich wie die Indizierung, allerdings kann in den eckigen Klammern die Syntax `[start:stop:step]` angewendet werden. Dabei bezeichnet
- `start` den Index des ersten Elements ab und inklusive dem ausgeschnitten werden soll (Default = 0)
- `stop` den Index des letzten Elements bis und exklusive **(!)** dem ausgeschnitten werden soll (Default = letztes Element)
- `step` wie of im Ausschnitt Elemente abgetasted werden sollen (Default = 1). Auch hier kann mit negativen Werten gearbeitet um rückwärts abzutasten.
:::: {.grid}
::: {.g-col-6}
```{python}
x = [1, 2, 3, 4, 5]
x[0:3]
```
:::
::: {.g-col-6}
```{python}
x = [1, 2, 3, 4, 5]
x[0:3:2]
```
:::
::::
Lässt man die Werte frei, werden die *defaults* angewendet:
:::: {.grid}
::: {.g-col-6}
```{python}
x = [1, 2, 3, 4, 5]
x[1:]
```
:::
::: {.g-col-6}
```{python}
x = [1, 2, 3, 4, 5]
x[::2]
```
:::
::::
## Hinzufügen
Mit `list.append()` kann ein Element ans Ende einer Liste hinzugefügt werden, während mit `list.insert()` ein Element an einer bestimmten Position (=Index; siehe weiter unten) eingefügt werden kann.
:::: {.grid}
::: {.g-col-6}
```{python}
x = [1, 2, 3]
x.append(10)
x # enthält nun 10 am Ende
```
:::
::: {.g-col-6}
```{python}
x = [1, 2, 3]
x.insert(1, 33)
x # enthält nun 33 an Index 1
```
:::
::::
Wenn nicht nur ein einzelnes Element, sondern z.B. eine andere Liste zu einer bestehenden Liste hinzugefügt werden soll, so funktioniert dies entweder schlicht mit einem `+` oder mit `list.extend()`. Der Unterschied ist, dass `+` eine neue Liste erzeugt, während `list.extend()` die bestehende Liste verändert. Wir demonstrieren dies mit Liste `b` von oben:
:::: {.grid}
::: {.g-col-6}
```{python}
x = [1, 2]
y = ['drei', True]
z = x + y
z
```
:::
::: {.g-col-6}
```{python}
x = [1, 2]
y = ['drei', True]
x.extend(y)
x
```
:::
::::
## Entfernen
Der Befehle `list.clear` entfernt alle Elemente aus einer Liste, während `list.remove()` (**nur!**) das erste passende Element aus einer Liste entfernt. Um ein Element an einer bestimmten Position zu entfernen, kann `del()` in Kombination mit dem Index (nicht dem zu entfernenden Wert!) verwendet werden.
:::: {.grid}
::: {.g-col-4}
```{python}
x = [1, 2, 3, 2, 1]
x.clear()
x
```
:::
::: {.g-col-4}
```{python}
x = [1, 2, 3, 2, 1]
x.remove(2)
x
```
:::
::: {.g-col-4}
```{python}
x = [1, 2, 3, 2, 1]
del(x[2])
x
```
:::
::::
Schließlich sei noch die Funktion `list.pop()` erwähnt, die stets das letzte Element einer Liste herauslöst, d.h. in der ursprünglichen Liste entfernt und gleichzeitig zurückgibt:
```{python}
x = [1, 2, 3]
y = x.pop()
print(y) # y ist nun 3 und
print(x) # x enthält 3 nicht mehr
```
## Verändern
Weitere gebräuchliche Listenfunktionen sind `list.sort()`, welche die Elemente einer Liste sortiert, und `list.reverse()`, welche die Reihenfolge der Elemente umkehrt. Beide Funktionen verändern die Liste, auf die sie angewendet werden. `list.sort()` kann auch mit dem Schlüsselwort `reverse = True` angewendet werden, um die Liste in umgekehrter Reihenfolge zu sortieren. Wir nutzen hier die `zahlen_liste = [1, 6, 3, 2]` von oben.
:::: {.grid}
::: {.g-col-4}
```{python}
x = [3, 1, 2]
x.reverse()
x
```
:::
::: {.g-col-4}
```{python}
x = [3, 1, 2]
x.sort()
x
```
:::
::: {.g-col-4}
```{python}
x = [3, 1, 2]
x.sort(reverse = True)
x
```
:::
::::
## Prüfen
Es ist oft hilfreich bestimmte Attribute von Listen zu extrahieren bzw. zu prüfen. Die Länge einer Liste, also die Anzahl der in ihr enthaltenen Elemente, kann mittels `len()` ausgegeben werden und wird auch häufig für weitere Operationen benötigt. Ebenso können die Schlüsselwörter `in` und `not` verwendet werden um zu prüfen ob ein Element bzw. ob ein Element nicht in einer Liste enthalten ist. Schließlich kann auch mittels `list.count()` gezählt werden wie oft ein Element in einer Liste enthalten ist.
:::: {.grid}
::: {.g-col-3}
```{python}
x = [42, 1, 42]
len(x)
```
:::
::: {.g-col-3}
```{python}
x = [42, 1, 42]
42 in x
```
:::
::: {.g-col-3}
```{python}
x = [42, 1, 42]
42 not in x
```
:::
::: {.g-col-3}
```{python}
x = [42, 1, 42]
x.count(42)
```
:::
::::
Gegeben, dass die Liste ausschließlich Zahlen enthält, können auch einfache Rechenoperationen wie Bestimmung des Maximums, Minimums oder der Summe durchgeführt werden.
:::: {.grid}
::: {.g-col-4}
```{python}
x = [3, 1, 2]
max(x)
```
:::
::: {.g-col-4}
```{python}
x = [3, 1, 2]
min(x)
```
:::
::: {.g-col-4}
```{python}
x = [3, 1, 2]
sum(x)
```
:::
::::
## Kopieren
Wie bereits erwähnt, sind Listen in Python mutable {{< fa chalkboard >}} Objekte. Natürlich kann es aber auch gewünscht sein eine Kopie einer Liste zu erzeugen, die eben nicht dauerhaft auf eine andere Liste verweist, sondern ab dem Moment eine echte, unabhängige Kopie darstellt, mit der separat weitergearbeitet werden kann. Zumindest bedingt **(!)** ist dies möglich via `list.copy()`:
:::: {.grid}
::: {.g-col-6}
```{python}
liste1 = [1, 2, 3]
liste2 = liste1
liste2.append(4)
print(liste1)
print(liste2)
```
:::
::: {.g-col-6}
```{python}
liste3 = [1, 2, 3]
liste4 = liste3.copy()
liste4.append(4)
print(liste3)
print(liste4)
```
:::
::::
Wie zu sehen wird - im Gegensatz zum Beispiel mit `liste1` und `liste2` - das Objekt `liste3` eben nicht durch das Anwenden der `append()` Funktion auf `liste4` verändert. Wie zu erwarten verhält sich `liste4` also als unabhängige Kopie von `liste3`. Dies stimmt leider wie gesagt nur bedingt, da `list.copy()` eine sogenannte flache (*shallow*) Kopie erzeugt. Dies meint, dass das Erzeugen von unabhängigen Kopien nur auf der obersten Listen-Ebene erfolgt. Sobald also eine Liste geschachtelt ist und auf den unteren Ebenen weitere mutable Objekte enthält, sind diese weiterhin nicht unabhängig. Um auch diese unabhängig zu machen, also eine tiefe (*deep*) Kopie zu erzeugen, kann die Funktion `copy.deepcopy()` aus dem `copy`-Modul verwendet werden. Es folgen drei Beispiele, die dies verdeutlichen sollen. Die Beispiele mit `listeA` bis `listeD` entsprechen dabei der Vorgehensweise von oben, wobei mit `listeF` die tiefe Kopie von `listeE` erzeugt wird. In allen Fällen wird sowohl ein Element in der obersten Listenebene, als auch in der Listenebene darunter verändert, sodass sich die Ergebnisse aller drei Beispiele unterscheiden:
:::: {.grid}
::: {.g-col-4}
```{python}
# kein extra Modul nötig
listeA = [[1,2], [3,4]]
listeB = listeA
listeB[1].append(5)
listeB.append(6)
print(listeA)
print(listeB)
```
:::
::: {.g-col-4}
```{python}
# kein extra Modul nötig
listeC = [[1,2], [3,4]]
listeD = listeC.copy()
listeD[1].append(5)
listeD.append(6)
print(listeC)
print(listeD)
```
:::
::: {.g-col-4}
```{python}
import copy
listeE = [[1,2], [3,4]]
listeF = copy.deepcopy(listeE)
listeF[1].append(5)
listeF.append(6)
print(listeE)
print(listeF)
```
:::
::::
::: callout-tip
## Weitere Ressourcen
- [{{< fa brands youtube >}} Mutable und Immutable in Python](https://youtu.be/DrdYOSjqozc?feature=shared)
- [{{< fa solid newspaper >}} Mutable vs Immutable Objects in Python](https://medium.com/@meghamohan/mutable-and-immutable-side-of-python-c2145cf72747)
- [{{< fa brands youtube >}} Python Tutorial deutsch 14/24 - Einführung in Listen](https://youtu.be/ihF8bZoauBs?si=cEvQIRy0tyF0L_4I)
- [{{< fa brands youtube >}} Python Tutorial deutsch 15/24 - Zugriff auf Listen](https://youtu.be/_XzWPXvya2w?si=ial_ZbV7HKmnPTgs)
- [{{< fa brands youtube >}} ALL 11 LIST METHODS IN PYTHON EXPLAINED](https://youtu.be/0yySumZTxJ0?si=JHJ0Xt3nJJp-_3Tg)
- [{{< fa brands python >}} More on Lists](https://docs.python.org/3/tutorial/datastructures.html#more-on-lists)
:::
# Übungen
::: {.webex-check .webex-box}
```{r}
#| echo: false
#| results: asis
q <- "Erzeuge drei unterschiedlich lange Listen, deren Länge jeweils identisch zu ihrer Summe ist. Keine der Listen darf nur aus Einsen bestehen."
q_choices <- c(answer="Geschafft")
cat(q,longmcq(q_choices))
q <- "Erzeuge eine Liste, deren Länge, Summe und Maximalwert identisch ist."
q_choices <- c(answer="Geschafft")
cat(q,longmcq(q_choices))
q <- "Erstelle ein separates Jupyter Notebook, in welchem du zu Beginn drei Listen mit den Namen 'a', 'b' und 'c' und einem Inhalt deiner Wahl erstellst. Daraufhin soll jede der in diesem Kapitel genannten Funktionalitäten mindestens einmal auf mindestens eine der Listen angewendet werden. Es gilt also alle Methoden/Funktionen nacheinander anzuwenden, ohne jemals mehr als drei Listen zu verwenden. Diese Übung soll einerseits die Anwendung der Methoden/Funktionen vertiefen und andererseits die Veränderung von mutable Listen durch die Methoden/Funktionen verdeutlichen. Strukturiere das Jupyter Notebook mithilfe von Überschriften und kurzen Erläuterungen so, dass es für jeden Schritt kurz erläutert was geschieht - ähnlich diesem Kapitel hier. Ziel ist es, dass du dieses Dokument später als Referenz nutzen kannst, falls du dich mal nicht mehr an diese Methoden erinnern solltest."
q_choices <- c(answer="Geschafft")
cat(q,longmcq(q_choices))
```
:::