# Iterables

> 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/iterierbare-objekte
Source: https://raw.githubusercontent.com/nakafaai/aksara/992eb09ded1422859aca3966c80b52a16b2832f4/packages/corpus/material/lesson/ai-ds/ai-programming/iterable/de.mdx

Nutze Python-Iterables in Schleifen, für Mitgliedschafts- und Wahrheitsprüfungen sowie zum Entpacken nach Position.

---

## Elemente eines Objekts nacheinander abrufen

In Python ist ein Iterable ein Objekt, das einer Schleife oder einem anderen Verbraucher **Elemente nacheinander** liefern kann. Strings, Listen, Tupel, Mengen, Dictionaries, Dateien und Generatoren sind alle iterierbar, obwohl sie ihre Elemente unterschiedlich ordnen und erzeugen.

Ein Iterable muss weder geordnet noch indizierbar, wiederverwendbar oder endlich sein. `iter()` muss einen Iterator liefern, über den sich Elemente nacheinander abrufen lassen. Ein endlicher Iterator stoppt, sobald er erschöpft ist.

### Typen von Iterables

Strings, Listen und Tupel liefern **ein Element pro Schleifendurchlauf**. Die Beispiele weisen der Schleifenvariablen jeweils ein Zeichen, eine Zahl oder eine Koordinate zu.

File: iterable_examples.py
```python
# String als iterierbar
text = "hello"
print("Zeichen in der Zeichenfolge:")
for char in text:
  print(char)
# Ausgabe:
# Zeichen in der Zeichenfolge:
# h
# e
# l
# l
# o

print()

# Liste als Iterable
numbers = [1, 2, 3, 4, 5]
print("Elemente in der Liste:")
for num in numbers:
  print(num)
# Ausgabe:
# Elemente in der Liste:
# 1
# 2
# 3
# 4
# 5

print()

# Tupel als iterierbar
coordinates = (10, 20, 30)
print("Elemente im Tupel:")
for coord in coordinates:
  print(coord)
# Ausgabe:
# Elemente im Tupel:
# 10
# 20
# 30
```

Der String liefert Zeichen in Textreihenfolge. Listen und Tupel liefern ihre gespeicherten Elemente in Positionsreihenfolge. Andere Iterable-Typen können anderen Ordnungsregeln folgen.

Eine Liste lässt sich erneut durchlaufen, doch ein Generatorobjekt **merkt sich den Iterationsfortschritt**. Der erste Aufruf von `list(iterator)` verbraucht den Generator unten. Der zweite startet die Berechnung nicht neu und liefert daher eine leere Liste.

File: iterator_consumption.py
```python
numbers = [1, 2, 3]
iterator = (number * 2 for number in numbers)
print(list(iterator))  # Output: [2, 4, 6]
print(list(iterator))  # Output: []
print(list(numbers))   # Output: [1, 2, 3]
```

## Mitgliedschaftsoperatoren

Die Operatoren `in` und `not in` fragen, ob ein Container **einen bestimmten Wert enthält**. Jeder Container-Typ legt fest, wie die Mitgliedschaft geprüft wird: Eine Liste durchsucht ihre Elemente, ein Dictionary sucht unter den Schlüsseln, und ein String sucht nach einem Teilstring.

### Verwendung von In und Not In

Listen und Tupel vergleichen ihre Elemente bis zu einem Treffer oder bis zum Ende. Strings suchen Teilstrings. Sets und Dictionaries verwenden ihr eigenes Suchverfahren. Bei einem Dictionary prüft `in` **Schlüssel, keine Werte**.

File: membership_operators.py
```python
# Mitgliedschaft in einer Liste prüfen
fruits = ["Apfel", "Orange", "Mango"]

# Den Operator in verwenden
if "Apfel" in fruits:
  print("Apfel ist in der Obstliste verfügbar")
# Ausgabe: Apfel ist in der Obstliste verfügbar

# Den Operator not in verwenden
if "durian" not in fruits:
  print("Durian ist in der Obstliste nicht verfügbar")
# Ausgabe: Durian ist in der Obstliste nicht verfügbar

print()

# Mitgliedschaft in einem String prüfen
message = "Python ist eine Programmiersprache"

# Suche nach Teilzeichenfolge in Zeichenfolge
if "Python" in message:
  print("Python-Wort in Nachricht gefunden")
# Ausgabe: Python-Wort in Nachricht gefunden

if "Java" not in message:
  print("Java-Wort in der Nachricht nicht gefunden")
# Ausgabe: Java-Wort in der Nachricht nicht gefunden
```

### Sonderfälle in Strings

Bei Strings darf der linke Operand ein einzelnes Zeichen oder ein längerer Teilstring sein. Der Test unterscheidet **Groß- und Kleinschreibung** und sucht nach einem zusammenhängenden Vorkommen.

File: string_membership.py
```python
# Zeichen in einem String suchen
text = "programming"

# Suche nach einzelnen Zeichen
print("g" in text)        # Ausgabe: True
print("z" in text)        # Ausgabe: False

print()

# Suche nach Teilzeichenfolge in Zeichenfolge
print("gram" in text)     # Ausgabe: True
print("program" in text)  # Ausgabe: True
print("java" in text)     # Ausgabe: False

# Nur Markierungen prüfen, keine vollständige E-Mail-Validierung
email = "user@example.com"
if "@" in email and ".com" in email:
  print("Enthält grundlegende E-Mail-Markierungen")
# Ausgabe: Enthält grundlegende E-Mail-Markierungen
```

Die letzte Prüfung demonstriert nur zwei Markierungen. Sie reicht nicht als E-Mail-Validierung, weil viele ungültige Adressen sowohl `@` als auch `.com` enthalten und viele gültige Adressen eine andere Domainendung verwenden.

## Die Funktionen Any und All

Die eingebauten Funktionen `any()` und `all()` verbrauchen ein Iterable und prüfen den Wahrheitswert seiner Elemente. Beide <Highlight>stoppen, sobald das Ergebnis feststeht</Highlight>. Generatorausdrücke vermeiden deshalb eine unnötige Zwischenliste.

### Die Funktion Any

`any()` gibt nach dem **ersten wahren Element** `True` zurück. Nur wenn alle Elemente falsch sind oder das Iterable leer ist, lautet das Ergebnis `False`.

File: any_function.py
```python
# Beispiel mit any()
grades = [60, 75, 45, 80]

# Prüfe, ob Werte über 70 vorhanden sind
print("Hohe Noten vorhanden:", any(grade > 70 for grade in grades))
# Ausgabe: Hohe Noten vorhanden: True

# Beispiel mit direktem booleschen Wert
conditions = [False, False, True, False]
print("Mindestens eine Bedingung ist wahr:", any(conditions))
# Ausgabe: Mindestens eine Bedingung ist wahr: True

# Beispiel mit leerer Liste
empty_list = []
print("any() für eine leere Liste:", any(empty_list))
# Ausgabe: any() für eine leere Liste: False

# Praxisbeispiel
numbers = [0, 0, 5, 0]
if any(numbers):
  print("In der Liste sind Zahlen ungleich Null vorhanden")
# Ausgabe: In der Liste sind Zahlen ungleich Null vorhanden
```

### Die Funktion All

`all()` gibt nach dem **ersten falschen Element** `False` zurück. Endet die Iteration ohne ein solches Element, lautet das Ergebnis `True`. Das erklärt auch den leeren Fall: Es gibt kein Element, das die Bedingung verletzt.

File: all_function.py
```python
# Beispiel mit all()
scores = [85, 90, 78, 92]

# Prüfe, ob alle Werte über 70 liegen
print("Alle Noten bestehen:", all(score >= 70 for score in scores))
# Ausgabe: Alle Noten bestehen: True

# Beispiel mit direktem booleschen Wert
conditions = [True, True, True, True]
print("Alle Bedingungen wahr:", all(conditions))
# Ausgabe: Alle Bedingungen wahr: True

# Beispiel mit einer falschen Bedingung
mixed_conditions = [True, True, False, True]
print("Alle Bedingungen wahr:", all(mixed_conditions))
# Ausgabe: Alle Bedingungen wahr: False

# Praxisbeispiel
ages = [18, 21, 25, 30]
if all(age >= 18 for age in ages):
  print("Alle Teilnehmer sind Erwachsene")
# Ausgabe: Alle Teilnehmer sind Erwachsene

print(all([]))  # Output: True
```

## Entpackoperator

In einem Funktionsaufruf erweitert ein vorangestelltes Sternchen (`*`) ein Iterable zu **Positionsargumenten**. Die Anzahl der entstandenen Argumente muss weiterhin zur aufgerufenen Funktion passen.

### Problem ohne Entpackoperator

Eine Funktion, die getrennte Koordinaten erwartet, erhält eine **Liste als einziges Argument**, wenn du sie nicht entpackst. Manuelle Indizes funktionieren bei einer festen Form, wiederholen aber die Containerstruktur an jeder Aufrufstelle.

File: unpacking_problem.py
```python
import math

# Problem: Die math.hypot-Funktion benötigt separate Argumente
coordinates = [3, 4]

# Falscher Weg - führt zu einem Fehler
try:
  result = math.hypot(coordinates)
  print(result)
except TypeError as e:
  print("Error:", e)
# Ausgabe: Error: must be real number, not list

# Manueller Weg, der für viele Argumente mühsam ist
result = math.hypot(coordinates[0], coordinates[1])
print("Manuelles Ergebnis:", result)
# Ausgabe: Manuelles Ergebnis: 5.0

# Beispiel mit mehr Argumenten
points = [1, 1, 1, 1]
# Der manuelle Weg wird sehr mühsam
result = math.hypot(points[0], points[1], points[2], points[3])
print("Ergebnis mit vier Argumenten:", result)
# Ausgabe: Ergebnis mit vier Argumenten: 2.0
```

### Lösung mit dem Entpackoperator

Ein `*` vor dem Iterable im Aufruf übergibt jedes erzeugte Element als **eigenes Positionsargument**.

File: unpacking_solution.py
```python
import math

# Lösung mit Auspackoperator
coordinates = [3, 4]
result = math.hypot(*coordinates)
print("Abstand vom Ursprung:", result)
# Ausgabe: Abstand vom Ursprung: 5.0

# Beispiel mit Tupel
point_3d = (1, 2, 2)
distance = math.hypot(*point_3d)
print("3D-Abstand:", distance)
# Ausgabe: 3D-Abstand: 3.0

# Beispiel mit vielen Argumenten
dimensions = [2, 3, 6, 1, 4]
euclidean_distance = math.hypot(*dimensions)
print("Euklidischer Abstand:", euclidean_distance)
# Ausgabe: Euklidischer Abstand: 8.12403840463596

# Weiteres praktisches Beispiel
def calculate_average(a, b, c):
  return (a + b + c) / 3

grades = [85, 90, 78]
average = calculate_average(*grades)
print("Durchschnittsnote:", average)
# Ausgabe: Durchschnittsnote: 84.33333333333333
```

### Praktische Anwendungen des Entpackens

Dieselbe Syntax funktioniert in Funktionsaufrufen und Collection-Darstellungen. Verwende sie, wenn die Schnittstelle **einzelne erweiterte Elemente** erwartet. Nimmt die Funktion ein Iterable an, übergibst du dieses direkt.

File: unpacking_practical.py
```python
# Entpacken für die print-Funktion
items = ["Apfel", "Orange", "Mango"]
print("Früchte:", *items)
# Ausgabe: Früchte: Apfel Orange Mango

# Feste Argumente für range() entpacken
bounds = [2, 10, 2]
print("Gerade Werte:", list(range(*bounds)))
# Ausgabe: Gerade Werte: [2, 4, 6, 8]

# Auspacken, um Listen zu kombinieren
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = [*list1, *list2]
print("Kombinierte Liste:", combined)
# Ausgabe: Kombinierte Liste: [1, 2, 3, 4, 5, 6]

# Funktion zum Entpacken im String-Format
template = "Name: {}, Alter: {}, Stadt: {}"
data = ["Alice", 25, "Jakarta"]
formatted = template.format(*data)
print(formatted)
# Ausgabe: Name: Alice, Alter: 25, Stadt: Jakarta
```