# Unveränderlich, veränderlich und Identität

> For AI agents: use [llms.txt](https://nakafa.com/llms.txt) for the site index. Markdown versions are available by appending `.md` to content URLs or sending `Accept: text/markdown`.

URL: https://nakafa.com/de/faecher/ki-und-data-science/ki-programmierung/unveraenderlich-veraenderlich-identitaet
Source: https://raw.githubusercontent.com/nakafaai/aksara/16d6b8e869d1a277313c65bbfc4b4a83efe77a46/packages/corpus/material/lesson/ai-ds/ai-programming/immutable-mutable-identity/de.mdx

Veränderbarkeit von Python-Objekten, Identität, Listenreferenzen und Speicherverwaltung anhand praktischer Beispiele und Code.

---

## Listen und Veränderlichkeit

In Python sind Listen Datenstrukturen, die nach ihrer Erstellung geändert werden können. Wir wollen verstehen, wie sich Listen verhalten, wenn wir bestimmte Vorgänge ausführen.

Wird eine Liste einer Variablen zugewiesen, verweist die Variable auf dieses Listenobjekt. Die Zuweisung kopiert die Liste nicht. Eine weitere Variable oder ein Container kann daher auf genau dasselbe Objekt verweisen.

File: list_reference.py
```python
# Liste erstellen und Referenz bereitstellen
list1 = [0, 1, 2]
list2 = ['a', list1, True]

print(list2)  # ['a', [0, 1, 2], True]

# Elemente in Liste1 ändern
list1[1] = [3, 4, 5]
print(list2)  # ['a', [0, [3, 4, 5], 2], True]
```

Beachten Sie, dass sich Änderungen an `list1` auch auf `list2` auswirken. Dies liegt daran, dass `list2` keine Kopie von `list1` speichert, sondern einen Verweis auf dasselbe Listenobjekt im Speicher.

### Ein vollständiger Slice erzeugt eine flache Kopie

Ein vollständiger Slice erzeugt eine neue äußere Liste mit Verweisen auf dieselben Elemente. Das ist eine flache Kopie. Wird ein äußeres Element ersetzt, bleibt die ursprüngliche Liste unverändert. Ein verschachteltes veränderliches Element wird jedoch weiterhin gemeinsam genutzt.

File: slice_copy.py
```python
# Slice-Operationen erstellen neue Listen
list1 = [0, 1, 2]
list2 = list1[:]  # Alle Elemente mit Slice nehmen

print(id(list1))  # Beispiel: 3104
print(id(list2))  # Beispiel: 8864 (anders als list1)

# Das Ändern von Elementen in Liste2 hat keine Auswirkungen auf Liste1
list2[0] = 'x'
print(list1)  # [0, 1, 2] (unverändert)
print(list2)  # ['x', 1, 2]

# Verschachtelte veränderliche Elemente bleiben gemeinsam genutzt
nested1 = [[1], [2]]
nested2 = nested1[:]
nested2[0].append(3)
print(nested1)  # [[1, 3], [2]]
```

## Objektidentität, -typ und -wert

Jedes Objekt in Python weist drei wichtige Merkmale auf, die es von anderen Objekten unterscheiden.

Component: Mermaid
Props:
- title: Identität zeigt an, welches Objekt verwendet wird
- description: Unterscheide Identität, Typ und Wert, damit ein Objekt nicht mit dem Variablennamen verwechselt wird, der darauf verweist.
```mermaid

  flowchart LR
      A[Python-Objekt] --> B[Identität]
      A --> C[Typ]
      A --> D[Wert]

      B --> B1[Während der Lebensdauer stabil]
      C --> C1[Bestimmt mögliche Operationen]
      D --> D1[Gespeicherte Daten]

```

### Objektidentität

Die Identität beantwortet, ob zwei Verweise auf dasselbe Objekt zeigen. `id()` liefert eine Ganzzahl, die unter gleichzeitig lebenden Objekten eindeutig ist. `is` vergleicht die Identität direkt. Die Sprache garantiert nicht, dass diese Ganzzahl eine physische Speicheradresse ist.

File: object_identity.py
```python
a = 300
print(a)          # 300
print(type(a))    # <class 'int'>
print(id(a))      # 10120

b = a  # b verweist auf dasselbe Objekt wie a
print(id(b))      # 10120 (wie a)

a = a + 1  # Erstellt neues Objekt
print(id(a))      # 10492 (anders als zuvor)
```

### Identität und Wert vergleichen

Verwende `is`, wenn es um die Identität eines Objekts geht, besonders bei Prüfungen wie `value is None`. Verwende `==`, wenn Werte verglichen werden sollen. Interpreter-Caches und Constant Folding können dazu führen, dass gleiche unveränderliche Werte dieselbe Identität haben. An dieses Implementierungsdetail darf Anwendungslogik jedoch nicht gekoppelt sein.

File: identity_comparison.py
```python
a = [1, 2]
b = a
c = [1, 2]

print(a is b)  # True: dasselbe Objekt
print(a == b)  # True: derselbe Wert
print(a is c)  # False: verschiedene Objekte
print(a == c)  # True: gleiche Werte
```

## Veränderliche und unveränderliche Objekte

Python klassifiziert Objekte anhand ihrer Fähigkeit, nach der Erstellung geändert zu werden.

### Unveränderliche Objekte

Unveränderliche Objekte können nach der Erstellung nicht mehr geändert werden. Jede Operation, die scheinbar das Objekt verändert, erstellt tatsächlich ein neues Objekt.

File: immutable_objects.py
```python
# Ganzzahl ist unveränderlich
a = 300
print(id(a))  # 10120

a = a + 1     # Erstellt neues Objekt
print(id(a))  # 10492 (anders)

# String ist auch unveränderlich
str_var = 'hello'
print(id(str_var))  # 75568

# Der Versuch, die Zeichenfolge zu ändern, führt zu einem Fehler
# str_var[1] = 'a'  # TypeError
```

Zu den häufigen unveränderlichen Typen in Python gehören:

- `bool` (boolesch)
- `int` (Ganzzahl)
- `float` (Gleitkommazahl)
- `complex` (komplexe Zahl)
- `str` (String)
- `tuple` (Tupel)

Die eigene Folge von Verweisen eines Tupels kann nicht verändert werden. Ein Objekt, auf das das Tupel verweist, kann dennoch veränderlich sein.

### Veränderliche Objekte

Veränderbare Objekte können nach der Erstellung geändert werden, ohne dass ein neues Objekt erstellt werden muss. Die Identität des Objekts bleibt gleich, auch wenn sich sein Wert ändert.

File: mutable_objects.py
```python
# Liste ist veränderbar
list_var = [0, 1, 2]
original_id = id(list_var)

# Das Ändern eines Elements erhält die Identität der Liste
list_var[0] = 9
print(id(list_var) == original_id)  # True

# Eine Zuweisung bindet die Variable an ein neues Objekt
list_var = [0, 0, 0]
print(id(list_var) == original_id)  # False

# Eine Slice-Zuweisung verändert die aktuelle Liste direkt
current_id = id(list_var)
list_var[:] = ['a', 'b', 'c']
print(id(list_var) == current_id)  # True
```

Zu den häufigen veränderlichen Typen in Python gehören:

- `list` (Liste)
- `dict` (Dictionary)
- `set` (Menge)

### Zuweisung vs. Änderungsverhalten

Es ist wichtig, zwischen Zuweisung (Gewährung eines neuen Werts) und direkter Änderung zu unterscheiden.

File: assignment_vs_modification.py
```python
list1 = [0, 1, 2]
list2 = list1  # Beide Variablen verweisen auf dasselbe Objekt

print(id(list1))     # 700
print(id(list2))     # 700 (gleich)

# Die direkte Änderung wirkt sich auf beide Variablen aus
list1[1] = 'x'
print(list1)         # [0, 'x', 2]
print(list2)         # [0, 'x', 2] (auch geändert)

# Zuweisung erstellt neue Referenz
list1 = [1, 2, 3]
print(list1)         # [1, 2, 3]
print(list2)         # [0, 'x', 2] (unverändert)
```

Component: Mermaid
Props:
- title: Zuweisung bindet neu, Mutation verändert
- description: Vergleiche eine Zuweisung, die einen Namen neu bindet, mit einer Mutation am bestehenden Objekt.
```mermaid

  flowchart TD
      subgraph Vorher ["Vor der Änderung"]
          A[list1] --> C["Listenobjekt: 0, 1, 2"]
          B[list2] --> C
      end

      subgraph Nachher ["Nach list1[1] = x"]
          D[list1] --> F["Dasselbe Listenobjekt: 0, x, 2"]
          E[list2] --> F
      end

      Vorher -.-> Nachher

```

Mutabilität entscheidet, ob eine Operation ein vorhandenes Objekt verändert oder einen Namen an ein anderes Objekt bindet. Unveränderlichkeit beseitigt eine Quelle von Fehlern bei gemeinsamem Zustand, macht ein Programm allein aber noch nicht threadsicher. Verwiesene veränderliche Objekte, zusammengesetzte Operationen und externe Ressourcen benötigen weiterhin eine bewusst gewählte Synchronisierung.