Python – ausführliche Anleitung und Nachschlagewerk

Python ist eine universell einsetzbare Programmiersprache, die sich durch eine vergleichsweise klare, gut lesbare Syntax auszeichnet. Sie eignet sich hervorragend für Automatisierung, Datenverarbeitung, Webentwicklung, wissenschaftliche Anwendungen, kleine Hilfsprogramme, Kommandozeilenwerkzeuge, Serveranwendungen, Raspberry-Pi-Projekte, Elektronik, Schnittstellenkommunikation und viele weitere Aufgaben.

Eine der großen Stärken von Python besteht darin, dass einfache Programme tatsächlich einfach aussehen können. Gleichzeitig wächst die Sprache problemlos mit: Vom fünfzeiligen Skript zum automatischen Zusammenführen von CSV-Dateien bis hin zu umfangreichen Anwendungen mit Datenbanken, Webservern oder Hardwareansteuerung ist vieles mit derselben Sprache möglich.

Diese Übersicht ist nicht bloß als Cheatsheet gedacht, sondern als kompaktes deutschsprachiges Nachschlagewerk. Die Beispiele konzentrieren sich bewusst auf jene Sprachmittel und Werkzeuge, die man in echten Projekten regelmäßig benötigt.

  1. Grundlagen und Syntax
  2. Variablen und Zuweisungen
  3. Datentypen
  4. Operatoren
  5. Zeichenketten
  6. Listen
  7. Tupel
  8. Dictionaries
  9. Sets
  10. Indexierung und Slicing
  11. Bedingungen
  12. Schleifen
  13. Comprehensions
  14. Funktionen
  15. Gültigkeitsbereiche
  16. Fehler und Exceptions
  17. Dateien lesen und schreiben
  18. Dateien und Pfade mit pathlib
  19. CSV-Dateien
  20. JSON-Dateien
  21. Module und Imports
  22. main() und __name__
  23. Objektorientierung
  24. Dataclasses
  25. Iteratoren und Generatoren
  26. Context Manager
  27. Type Hints
  28. Datum und Uhrzeit
  29. Reguläre Ausdrücke
  30. Virtuelle Umgebungen
  31. Pakete mit pip
  32. Kommandozeilenprogramme
  33. Logging
  34. Tests
  35. Debugging
  36. Programme als EXE verteilen
  37. Codequalität und Stil
  38. Typische Fehler und Fallen
  39. Sinnvolle Projektstruktur
  40. Praktische Regeln

1. Grundlagen und Syntax

Python wird üblicherweise als interpretierte Sprache verwendet. Ein Python-Programm wird in einer Datei mit der Endung .py gespeichert und anschließend vom Python-Interpreter ausgeführt.

Hello World

print("Hallo, Welt!")

print() gibt einen Wert im Terminal beziehungsweise in der Konsole aus.

Python-Datei ausführen

python programm.py

Je nach System wird auch folgender Befehl verwendet:

python3 programm.py

Einrückung ist Syntax

temperatur = 20

if temperatur > 15:
    print("Es ist mild.")

print("Programmende")

Python verwendet keine geschweiften Klammern für Codeblöcke. Die Einrückung bestimmt die Struktur des Programms.

Vier Leerzeichen

Als Konvention werden pro Einrückungsebene vier Leerzeichen verwendet.

if bedingung:
    if weitere_bedingung:
        print("Zwei Ebenen eingerückt")

Tabs und Leerzeichen wild zu vermischen ist eine zuverlässige Methode, sich selbst unnötige Schwierigkeiten zu bereiten.

Kommentare

# Das ist ein Kommentar.

temperatur = 21  # Kommentar am Zeilenende

Kommentare erklären vor allem das Warum. Offensichtlichen Code noch einmal in Prosa nachzuerzählen bringt wenig.

Docstrings

def berechne_flaeche(breite, hoehe):
    """
    Berechnet die Fläche eines Rechtecks.

    Parameter:
        breite: Breite des Rechtecks
        hoehe: Höhe des Rechtecks
    """
    return breite * hoehe

Dreifache Anführungszeichen werden häufig für Dokumentationsstrings von Funktionen, Klassen und Modulen verwendet.


2. Variablen und Zuweisungen

Variablen speichern Referenzen auf Werte. Anders als etwa in C oder Java muss der Datentyp einer Variablen normalerweise nicht ausdrücklich angegeben werden.

Einfache Variablen

name = "Julian"
alter = 28
temperatur = 21.5
ist_online = True

Mehrere Zuweisungen

x, y, z = 10, 20, 30

print(x)
print(y)
print(z)

Werte vertauschen

a = 10
b = 20

a, b = b, a

print(a)  # 20
print(b)  # 10

Eine temporäre Hilfsvariable ist dafür in Python nicht notwendig.

Konstanten

MAXIMALE_TEMPERATUR = 80
DATEINAME = "daten.csv"

Python besitzt keine echten Konstanten. Großgeschriebene Namen sind eine Konvention: „Dieser Wert soll nicht verändert werden.“

Variablennamen

temperatur_celsius = 22.5
anzahl_messungen = 17
csv_datei = "daten.csv"

Für Variablen und Funktionen wird normalerweise snake_case verwendet.


3. Datentypen

Wichtige elementare Datentypen

text = "Hallo"       # str
zahl = 42            # int
kommazahl = 3.1415   # float
aktiv = True         # bool
nichts = None        # NoneType

Datentyp prüfen

wert = 42

print(type(wert))

Ausgabe:

<class 'int'>

Datentypen umwandeln

text = "42"

zahl = int(text)
kommazahl = float(text)
neuer_text = str(zahl)

print(zahl + 8)

Boolean-Werte

aktiv = True
fehler = False

if aktiv:
    print("System aktiv")

Die Python-Werte heißen exakt True und False, jeweils mit großem Anfangsbuchstaben.

None

ergebnis = None

if ergebnis is None:
    print("Noch kein Ergebnis vorhanden")

None repräsentiert das Fehlen eines Wertes.

Mutable und immutable

Manche Python-Objekte können nach ihrer Erzeugung verändert werden, andere nicht.

  • Veränderbar: Listen, Dictionaries, Sets
  • Nicht veränderbar: Strings, Zahlen, Tupel

4. Operatoren

Arithmetische Operatoren

a + b    # Addition
a - b    # Subtraktion
a * b    # Multiplikation
a / b    # Division
a // b   # Ganzzahldivision
a % b    # Rest / Modulo
a ** b   # Potenz

Beispiel

print(10 / 3)   # 3.333...
print(10 // 3)  # 3
print(10 % 3)   # 1
print(2 ** 8)   # 256

Vergleichsoperatoren

x == 10   # gleich
x != 10   # ungleich
x > 10    # größer
x < 10    # kleiner
x >= 10   # größer oder gleich
x <= 10   # kleiner oder gleich

Logische Operatoren

if alter >= 18 and land == "AT":
    print("Bedingungen erfüllt")

if ist_admin or ist_editor:
    print("Bearbeitung erlaubt")

if not aktiv:
    print("Nicht aktiv")

Zuweisungsoperatoren

x = 10

x += 2
x -= 3
x *= 4
x /= 2

is und == sind nicht dasselbe

a == b   # Haben die Objekte denselben Wert?
a is b   # Sind es tatsächlich dieselben Objekte?

Für normale Wertevergleiche verwendet man ==. is wird insbesondere bei None verwendet.

if wert is None:
    print("Kein Wert")

5. Zeichenketten – Strings

Zeichenketten sind Folgen von Zeichen und gehören zu den meistverwendeten Datentypen überhaupt.

Strings erzeugen

text1 = "Hallo"
text2 = 'Python'

String verketten

vorname = "Max"
nachname = "Mustermann"

name = vorname + " " + nachname

print(name)

f-Strings

name = "Max"
alter = 28

print(f"{name} ist {alter} Jahre alt.")

f-Strings sind meistens die angenehmste Art, Werte in Text einzubauen.

Zahlen formatieren

temperatur = 21.5678

print(f"{temperatur:.1f} °C")
print(f"{temperatur:.2f} °C")

Ausgabe:

21.6 °C
21.57 °C

Nützliche String-Methoden

text = "  Hallo Python  "

print(text.strip())
print(text.lower())
print(text.upper())
print(text.replace("Python", "Welt"))
print(text.startswith("  Hallo"))
print(text.endswith("  "))

String aufteilen

daten = "Temperatur;Luftdruck;Feuchtigkeit"

teile = daten.split(";")

print(teile)

Liste zu String verbinden

woerter = ["Python", "ist", "praktisch"]

satz = " ".join(woerter)

print(satz)

Mehrzeilige Strings

text = """Erste Zeile
Zweite Zeile
Dritte Zeile"""

print(text)

Escape-Sequenzen

print("Zeile 1\nZeile 2")
print("Name:\tMax")
print("Er sagte: \"Hallo\"")
  • \n – Zeilenumbruch
  • \t – Tabulator
  • \" – Anführungszeichen
  • \\ – Backslash

Raw Strings

pfad = r"C:\Users\Max\Documents"

print(pfad)

Das Präfix r verhindert, dass Backslashes als Escape-Sequenzen interpretiert werden.


6. Listen

Listen speichern mehrere Werte in einer geordneten, veränderbaren Sammlung.

Liste erzeugen

farben = ["rot", "grün", "blau"]

print(farben)

Element abrufen

farben = ["rot", "grün", "blau"]

print(farben[0])
print(farben[1])
print(farben[-1])

Der erste Index ist 0. Negative Indizes zählen vom Ende.

Element hinzufügen

farben = ["rot", "grün"]

farben.append("blau")

Mehrere Elemente hinzufügen

farben = ["rot"]

farben.extend(["grün", "blau"])

Element einfügen

farben = ["rot", "blau"]

farben.insert(1, "grün")

Element entfernen

farben = ["rot", "grün", "blau"]

farben.remove("grün")

Element mit pop() entfernen

farben = ["rot", "grün", "blau"]

letztes_element = farben.pop()

print(letztes_element)

Länge bestimmen

farben = ["rot", "grün", "blau"]

print(len(farben))

Sortieren

zahlen = [5, 2, 8, 1]

zahlen.sort()

print(zahlen)

Absteigend:

zahlen.sort(reverse=True)

Neue sortierte Liste

zahlen = [5, 2, 8, 1]

sortiert = sorted(zahlen)

sort() verändert die vorhandene Liste. sorted() liefert eine neue Liste zurück.


7. Tupel

Tupel ähneln Listen, können nach ihrer Erstellung jedoch nicht verändert werden.

Tupel erzeugen

koordinaten = (47.0707, 15.4395)

print(koordinaten[0])

Tuple Unpacking

koordinaten = (47.0707, 15.4395)

breitengrad, laengengrad = koordinaten

print(breitengrad)
print(laengengrad)

Wann sind Tupel sinnvoll?

Tupel eignen sich besonders für zusammengehörige Werte, deren Struktur nicht verändert werden soll, beispielsweise Koordinaten oder RGB-Werte.


8. Dictionaries

Dictionaries speichern Werte als Schlüssel-Wert-Paare. Sie gehören zu den praktischsten Datenstrukturen in Python.

Dictionary erzeugen

person = {
    "name": "Max",
    "alter": 28,
    "stadt": "Graz"
}

Wert auslesen

print(person["name"])

get()

print(person.get("alter"))

print(person.get("beruf", "nicht angegeben"))

get() ist praktisch, wenn ein Schlüssel möglicherweise nicht vorhanden ist.

Wert ändern

person["alter"] = 29

Neuen Wert hinzufügen

person["beruf"] = "Entwickler"

Über Dictionary iterieren

for schluessel, wert in person.items():
    print(schluessel, wert)

Nur Schlüssel

for schluessel in person:
    print(schluessel)

Nur Werte

for wert in person.values():
    print(wert)

9. Sets

Sets sind ungeordnete Mengen eindeutiger Werte. Doppelte Elemente werden automatisch entfernt.

Set erzeugen

zahlen = {1, 2, 3, 3, 4}

print(zahlen)

Doppelte Werte entfernen

namen = ["Anna", "Max", "Anna", "Peter"]

eindeutig = set(namen)

print(eindeutig)

Mengenoperationen

a = {1, 2, 3}
b = {3, 4, 5}

print(a | b)  # Vereinigung
print(a & b)  # Schnittmenge
print(a - b)  # Differenz

10. Indexierung und Slicing

Mit Slicing lassen sich Ausschnitte aus Listen, Strings und anderen Sequenzen gewinnen.

Grundprinzip

werte = [10, 20, 30, 40, 50]

print(werte[1:4])

Ergebnis:

[20, 30, 40]

Der Startindex wird eingeschlossen, der Endindex nicht.

Vom Anfang

werte[:3]

Bis zum Ende

werte[2:]

Schrittweite

werte[::2]

Nimmt jedes zweite Element.

Reihenfolge umdrehen

werte[::-1]

11. Bedingungen

if

temperatur = 25

if temperatur > 20:
    print("Warm")

if / else

alter = 17

if alter >= 18:
    print("Volljährig")
else:
    print("Minderjährig")

if / elif / else

temperatur = 18

if temperatur > 25:
    print("Warm")
elif temperatur > 15:
    print("Mild")
elif temperatur > 5:
    print("Kühl")
else:
    print("Kalt")

Mehrere Bedingungen

temperatur = 22
sonnig = True

if temperatur > 20 and sonnig:
    print("Gutes Wetter")

Membership-Test mit in

farben = ["rot", "grün", "blau"]

if "grün" in farben:
    print("Grün ist vorhanden")

Truthy und Falsy

liste = []

if liste:
    print("Liste enthält Werte")
else:
    print("Liste ist leer")

Als falsch gelten unter anderem:

  • False
  • None
  • 0
  • 0.0
  • ""
  • []
  • {}
  • set()

Kurzform einer Bedingung

alter = 20

status = "volljährig" if alter >= 18 else "minderjährig"

print(status)

Das ist der sogenannte bedingte Ausdruck oder ternäre Ausdruck.


12. Schleifen

for-Schleife

for zahl in range(5):
    print(zahl)

Ausgabe: 0 bis 4.

range()

range(5)         # 0, 1, 2, 3, 4
range(2, 6)      # 2, 3, 4, 5
range(0, 10, 2)  # 0, 2, 4, 6, 8

Liste durchlaufen

farben = ["rot", "grün", "blau"]

for farbe in farben:
    print(farbe)

enumerate()

farben = ["rot", "grün", "blau"]

for index, farbe in enumerate(farben):
    print(index, farbe)

Damit erhält man gleichzeitig Index und Wert.

enumerate() ab 1

for nummer, farbe in enumerate(farben, start=1):
    print(nummer, farbe)

zip()

namen = ["Anna", "Max", "Peter"]
alter = [30, 28, 41]

for name, jahre in zip(namen, alter):
    print(name, jahre)

zip() verbindet mehrere Sequenzen elementweise.

while-Schleife

zaehler = 0

while zaehler < 5:
    print(zaehler)
    zaehler += 1

Bei while muss sichergestellt sein, dass die Abbruchbedingung irgendwann erreicht wird. Sonst entsteht eine Endlosschleife.

break

for zahl in range(100):
    if zahl == 10:
        break

    print(zahl)

break beendet die Schleife vollständig.

continue

for zahl in range(10):
    if zahl == 5:
        continue

    print(zahl)

continue überspringt lediglich den aktuellen Durchlauf.

Schleifen mit else

for zahl in range(5):
    if zahl == 10:
        break
else:
    print("Die Schleife wurde nicht mit break beendet.")

Diese Konstruktion ist weniger bekannt, kann bei Suchvorgängen aber elegant sein.


13. Comprehensions

Comprehensions erzeugen Collections kompakt aus anderen iterierbaren Objekten. Sie sind sehr praktisch, sollten aber lesbar bleiben.

List Comprehension

quadrate = [zahl ** 2 for zahl in range(10)]

print(quadrate)

Mit Bedingung

gerade = [
    zahl
    for zahl in range(20)
    if zahl % 2 == 0
]

Dictionary Comprehension

quadrate = {
    zahl: zahl ** 2
    for zahl in range(5)
}

Set Comprehension

reste = {
    zahl % 3
    for zahl in range(20)
}

Nicht übertreiben

Eine einfache Comprehension ist elegant. Eine dreifach verschachtelte Comprehension mit mehreren Bedingungen ist oftmals bloß eine gewöhnliche Schleife im Abendkleid. Lesbarkeit geht vor Kürze.


14. Funktionen

Funktionen zerlegen Programme in kleinere, wiederverwendbare Einheiten. Gut strukturierter Python-Code besteht normalerweise aus mehreren überschaubaren Funktionen statt aus einem gewaltigen linearen Skript.

Funktion definieren

def begruessung(name):
    print(f"Hallo, {name}!")

begruessung("Max")

Wert zurückgeben

def addiere(a, b):
    return a + b

ergebnis = addiere(5, 7)

print(ergebnis)

Mehrere Werte zurückgeben

def min_max(werte):
    return min(werte), max(werte)

minimum, maximum = min_max([4, 8, 1, 9])

Technisch wird dabei ein Tupel zurückgegeben.

Standardargumente

def begruessung(name="Welt"):
    print(f"Hallo, {name}!")

begruessung()
begruessung("Max")

Keyword-Argumente

def beschreibe(name, alter, stadt):
    print(name, alter, stadt)

beschreibe(
    stadt="Graz",
    name="Max",
    alter=28
)

*args

def summiere(*zahlen):
    return sum(zahlen)

print(summiere(1, 2, 3, 4, 5))

*args nimmt eine beliebige Anzahl positionsbasierter Argumente entgegen.

**kwargs

def anzeigen(**daten):
    for schluessel, wert in daten.items():
        print(schluessel, wert)

anzeigen(
    name="Max",
    alter=28,
    stadt="Graz"
)

**kwargs sammelt benannte Argumente in einem Dictionary.

Lambda-Funktionen

verdoppeln = lambda x: x * 2

print(verdoppeln(5))

Lambdas eignen sich für sehr kleine anonyme Funktionen, beispielsweise als Sortierschlüssel.

personen = [
    {"name": "Anna", "alter": 32},
    {"name": "Max", "alter": 25}
]

personen.sort(key=lambda person: person["alter"])

Docstring einer Funktion

def celsius_zu_fahrenheit(celsius):
    """Wandelt Celsius in Fahrenheit um."""
    return celsius * 9 / 5 + 32

15. Gültigkeitsbereiche von Variablen

Lokale Variable

def funktion():
    wert = 10
    print(wert)

funktion()

wert existiert in diesem Beispiel nur innerhalb der Funktion.

Globale Variable

temperatur = 20

def anzeigen():
    print(temperatur)

anzeigen()

global

zaehler = 0

def erhoehen():
    global zaehler
    zaehler += 1

Globale Variablen sind gelegentlich nützlich, in größeren Programmen aber häufig ein Zeichen dafür, dass die Struktur verbessert werden könnte.


16. Fehler und Exceptions

Fehler gehören zum Programmieren. Entscheidend ist nicht, sie vollständig zu vermeiden, sondern sie kontrolliert zu erkennen und sinnvoll zu behandeln.

try / except

try:
    zahl = int("abc")
except ValueError:
    print("Keine gültige Ganzzahl.")

Mehrere Fehlertypen

try:
    zahl = int(eingabe)
    ergebnis = 100 / zahl

except ValueError:
    print("Ungültige Zahl.")

except ZeroDivisionError:
    print("Division durch null ist nicht möglich.")

else

try:
    zahl = int("42")
except ValueError:
    print("Fehler")
else:
    print("Erfolgreich:", zahl)

else wird nur ausgeführt, wenn im try-Block keine Exception auftritt.

finally

try:
    print("Versuch")
finally:
    print("Dieser Block läuft immer.")

Eigene Exception auslösen

alter = -5

if alter < 0:
    raise ValueError("Das Alter darf nicht negativ sein.")

Nicht alles blind abfangen

try:
    irgendetwas()
except Exception as fehler:
    print(fehler)

Ein allgemeines except Exception kann sinnvoll sein, sollte aber bewusst eingesetzt werden. Präzise Fehlertypen sind meist besser.

Traceback lesen

Bei einem unbehandelten Fehler zeigt Python einen sogenannten Traceback an. Besonders wichtig sind:

  • Dateiname
  • Zeilennummer
  • Fehlertyp
  • Fehlermeldung

Der letzte Eintrag des Tracebacks ist meist der beste Ausgangspunkt für die Fehlersuche.


17. Dateien lesen und schreiben

Datei vollständig lesen

with open("notizen.txt", "r", encoding="utf-8") as datei:
    inhalt = datei.read()

print(inhalt)

Datei zeilenweise lesen

with open("daten.txt", "r", encoding="utf-8") as datei:
    for zeile in datei:
        print(zeile.strip())

Datei schreiben

with open("ausgabe.txt", "w", encoding="utf-8") as datei:
    datei.write("Hallo Welt!\n")

"w" überschreibt eine bereits vorhandene Datei.

An bestehende Datei anhängen

with open("log.txt", "a", encoding="utf-8") as datei:
    datei.write("Neuer Eintrag\n")

Dateimodi

  • r – lesen
  • w – schreiben und überschreiben
  • a – anhängen
  • x – neue Datei erzeugen, Fehler falls vorhanden
  • b – Binärmodus

Warum with?

Der with-Block sorgt dafür, dass die Datei zuverlässig geschlossen wird – auch wenn innerhalb des Blocks ein Fehler auftritt.


18. Dateien und Pfade mit pathlib

Für moderne Python-Programme ist pathlib oft angenehmer und lesbarer als das manuelle Zusammensetzen von Dateipfaden.

Path importieren

from pathlib import Path

Aktuelles Verzeichnis

from pathlib import Path

ordner = Path.cwd()

print(ordner)

Verzeichnis der Python-Datei

from pathlib import Path

basisordner = Path(__file__).resolve().parent

print(basisordner)

Das ist besonders nützlich, wenn ein Skript Dateien aus demselben Ordner wie die .py-Datei verarbeiten soll.

Pfade zusammensetzen

basisordner = Path(__file__).resolve().parent

datei = basisordner / "daten" / "messwerte.csv"

Dadurch muss man sich nicht selbst mit Windows- und Linux-Pfadtrennern herumschlagen.

Prüfen, ob Datei existiert

if datei.exists():
    print("Datei vorhanden")

Dateien suchen

ordner = Path("daten")

for datei in ordner.glob("*.csv"):
    print(datei)

Rekursiv suchen

for datei in ordner.rglob("*.csv"):
    print(datei)

Dateiname und Endung

datei = Path("messungen.csv")

print(datei.name)
print(datei.stem)
print(datei.suffix)

19. CSV-Dateien

Python besitzt mit dem Modul csv bereits in der Standardbibliothek Werkzeuge zum Lesen und Schreiben von CSV-Dateien.

CSV lesen

import csv

with open("daten.csv", "r", encoding="utf-8", newline="") as datei:
    reader = csv.reader(datei)

    for zeile in reader:
        print(zeile)

Semikolon als Trennzeichen

reader = csv.reader(
    datei,
    delimiter=";"
)

Gerade deutschsprachige Tabellenkalkulationen verwenden häufig Semikolon statt Komma als Feldtrenner.

CSV schreiben

import csv

daten = [
    ["Zeit", "Temperatur"],
    ["12:00", 21.5],
    ["13:00", 22.1]
]

with open("ausgabe.csv", "w", encoding="utf-8", newline="") as datei:
    writer = csv.writer(datei)

    writer.writerows(daten)

DictReader

import csv

with open("daten.csv", encoding="utf-8", newline="") as datei:
    reader = csv.DictReader(datei)

    for zeile in reader:
        print(zeile["Temperatur"])

DictReader verwendet die Kopfzeile als Schlüssel. Das macht Code häufig deutlich verständlicher.

Mehrere CSV-Dateien finden

from pathlib import Path

ordner = Path(__file__).resolve().parent

dateien = sorted(ordner.glob("*.csv"))

for datei in dateien:
    print(datei.name)

20. JSON

JSON ist eines der wichtigsten Austauschformate für strukturierte Daten und wird beispielsweise von Web-APIs, Konfigurationsdateien und vielen Programmen verwendet.

JSON-Datei lesen

import json

with open("daten.json", "r", encoding="utf-8") as datei:
    daten = json.load(datei)

print(daten)

JSON-Datei schreiben

import json

daten = {
    "name": "Sensor 1",
    "temperatur": 21.7
}

with open("daten.json", "w", encoding="utf-8") as datei:
    json.dump(
        daten,
        datei,
        indent=4,
        ensure_ascii=False
    )

JSON-String einlesen

import json

text = '{"name": "Sensor", "wert": 42}'

daten = json.loads(text)

Python zu JSON-String

text = json.dumps(
    daten,
    indent=4,
    ensure_ascii=False
)

21. Module und Imports

Python-Programme lassen sich auf mehrere Dateien aufteilen. Eine Python-Datei kann dabei als Modul in eine andere importiert werden.

Ganzes Modul importieren

import math

wurzel = math.sqrt(16)

print(wurzel)

Einzelne Funktion importieren

from math import sqrt

print(sqrt(25))

Alias verwenden

import datetime as dt

jetzt = dt.datetime.now()

Eigenes Modul

Datei berechnungen.py:

def addiere(a, b):
    return a + b

Andere Datei:

from berechnungen import addiere

print(addiere(2, 3))

Nützliche Standardmodule

import csv
import json
import math
import random
import statistics
import datetime
import logging
import argparse
import subprocess
import shutil
import re

from pathlib import Path

Wildcard-Import vermeiden

from modul import *

Diese Schreibweise sollte gewöhnlich vermieden werden, weil nicht mehr klar erkennbar ist, welche Namen aus welchem Modul stammen.


22. main() und __name__

Für ausführbare Python-Skripte ist es gute Praxis, den eigentlichen Programmeinstieg in eine Funktion namens main() zu legen.

Typischer Aufbau

def main():
    print("Programm wird ausgeführt.")


if __name__ == "__main__":
    main()

Warum?

Wird die Datei direkt ausgeführt, besitzt __name__ den Wert "__main__".

Wird dieselbe Datei hingegen importiert, wird main() nicht automatisch gestartet. Dadurch können Funktionen eines Skripts auch von anderen Programmen verwendet werden.


23. Objektorientierte Programmierung

Mit Klassen lassen sich Daten und dazugehörige Funktionen zu eigenen Objekttypen zusammenfassen.

Einfache Klasse

class Sensor:
    def __init__(self, name, temperatur):
        self.name = name
        self.temperatur = temperatur

    def anzeigen(self):
        print(
            f"{self.name}: "
            f"{self.temperatur} °C"
        )


sensor = Sensor("BME280", 21.5)

sensor.anzeigen()

__init__()

__init__() wird beim Erzeugen eines neuen Objektes ausgeführt und dient typischerweise dazu, dessen Attribute zu initialisieren.

self

self bezeichnet das konkrete Objekt, auf dem eine Methode aufgerufen wird.

self.name
self.temperatur

Vererbung

class Sensor:
    def messen(self):
        print("Messung")


class Temperatursensor(Sensor):
    def temperatur(self):
        return 21.5

__str__()

class Sensor:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f"Sensor: {self.name}"


sensor = Sensor("BME280")

print(sensor)

Spezielle Methoden mit doppelten Unterstrichen werden manchmal scherzhaft „dunder methods“ genannt.

Klassen sparsam einsetzen

Nicht jedes Programm benötigt Klassen. Bei kleinen Automatisierungsskripten sind einige klare Funktionen häufig die einfachere und bessere Lösung.


24. Dataclasses

Wenn eine Klasse hauptsächlich Daten speichern soll, reduziert @dataclass unnötigen Standardcode.

Einfache Dataclass

from dataclasses import dataclass


@dataclass
class Messwert:
    zeit: str
    temperatur: float
    luftdruck: float


messung = Messwert(
    zeit="12:00",
    temperatur=21.5,
    luftdruck=1015.2
)

print(messung)

25. Iteratoren und Generatoren

Generatoren erzeugen Werte erst dann, wenn sie benötigt werden. Dadurch können große Datenmengen verarbeitet werden, ohne alles gleichzeitig im Speicher zu halten.

Generator mit yield

def zahlen_bis(maximum):
    zahl = 1

    while zahl <= maximum:
        yield zahl
        zahl += 1


for zahl in zahlen_bis(5):
    print(zahl)

Generator Expression

quadrate = (
    x ** 2
    for x in range(1000000)
)

Die runden Klammern erzeugen hier keinen Tupel, sondern einen Generator.


26. Context Manager

Das bekannte with open(...) ist ein Beispiel für einen Context Manager. Context Manager verwalten Ressourcen zuverlässig.

Typischer Einsatz

with open("daten.txt", encoding="utf-8") as datei:
    inhalt = datei.read()

Sobald der Block verlassen wird, räumt der Context Manager die verwendete Ressource automatisch auf.


27. Type Hints

Python bleibt dynamisch typisiert, kann aber mit Typinformationen versehen werden. Diese erhöhen vor allem bei größeren Projekten die Verständlichkeit.

Typen für Variablen

name: str = "Max"
alter: int = 28
temperatur: float = 21.5
aktiv: bool = True

Funktionen typisieren

def addiere(a: int, b: int) -> int:
    return a + b

Listen typisieren

temperaturen: list[float] = [
    20.5,
    21.2,
    19.8
]

Optionaler Wert

def finde_sensor(name: str) -> str | None:
    if name == "BME280":
        return "gefunden"

    return None

Type Hints erzwingen nichts

Python kontrolliert Type Hints beim normalen Programmablauf nicht automatisch. Sie dienen Dokumentation, IDEs und Werkzeugen zur statischen Codeanalyse.


28. Datum und Uhrzeit

Aktuelles Datum

from datetime import date

heute = date.today()

print(heute)

Datum und Uhrzeit

from datetime import datetime

jetzt = datetime.now()

print(jetzt)

Formatieren

from datetime import datetime

jetzt = datetime.now()

text = jetzt.strftime("%d.%m.%Y %H:%M")

print(text)

Text in Datum umwandeln

from datetime import datetime

text = "18.08.2026"

datum = datetime.strptime(
    text,
    "%d.%m.%Y"
)

Zeitdifferenzen

from datetime import date

start = date(2026, 8, 1)
ende = date(2026, 8, 18)

differenz = ende - start

print(differenz.days)

29. Reguläre Ausdrücke

Reguläre Ausdrücke dienen dazu, Text nach Mustern zu durchsuchen oder zu zerlegen. Python stellt dafür das Modul re bereit.

Nach Muster suchen

import re

text = "Temperatur: 21.5 °C"

treffer = re.search(
    r"\d+\.\d+",
    text
)

if treffer:
    print(treffer.group())

Alle Treffer finden

import re

text = "Sensor 1, Sensor 2, Sensor 3"

zahlen = re.findall(r"\d+", text)

print(zahlen)

Typische Muster

\d     # Ziffer
\w     # Buchstabe/Ziffer/Unterstrich
\s     # Leerraum
.      # beliebiges Zeichen
+      # mindestens einmal
*      # beliebig oft
?      # optional
^      # Textanfang
$      # Textende

Raw Strings verwenden

muster = r"\d+\.\d+"

Reguläre Ausdrücke werden in Python fast immer als Raw Strings geschrieben.


30. Virtuelle Umgebungen

Eine virtuelle Umgebung isoliert die installierten Python-Pakete eines Projekts vom restlichen System. Das verhindert Versionskonflikte und gehört bei ernsthaften Python-Projekten zur normalen Grundhygiene.

Virtuelle Umgebung erzeugen

python -m venv .venv

Unter Linux häufig:

python3 -m venv .venv

Windows PowerShell aktivieren

.venv\Scripts\Activate.ps1

Windows CMD aktivieren

.venv\Scripts\activate.bat

Linux / Raspberry Pi

source .venv/bin/activate

Umgebung verlassen

deactivate

.venv nicht mit Git speichern

In die Datei .gitignore gehört normalerweise:

.venv/

31. Pakete mit pip verwalten

Mit pip können zusätzliche Python-Bibliotheken installiert werden, die nicht Bestandteil der Standardbibliothek sind.

Paket installieren

pip install requests

Robustere Schreibweise

python -m pip install requests

Damit ist eindeutiger, zu welcher Python-Installation der verwendete Paketmanager gehört.

Installierte Pakete anzeigen

pip list

Abhängigkeiten speichern

pip freeze > requirements.txt

Abhängigkeiten installieren

pip install -r requirements.txt

Paket entfernen

pip uninstall paketname

Paket aktualisieren

python -m pip install --upgrade paketname

32. Kommandozeilenprogramme mit argparse

Für Skripte, die Parameter über die Kommandozeile erhalten sollen, bietet Python das Modul argparse.

Einfaches Beispiel

import argparse


parser = argparse.ArgumentParser(
    description="Verarbeitet eine CSV-Datei."
)

parser.add_argument(
    "datei",
    help="Pfad zur CSV-Datei"
)

args = parser.parse_args()

print(args.datei)

Programm aufrufen

python programm.py daten.csv

Optionales Argument

parser.add_argument(
    "--ausgabe",
    default="ergebnis.csv",
    help="Name der Ausgabedatei"
)

Aufruf:

python programm.py daten.csv --ausgabe zusammen.csv

Automatische Hilfe

python programm.py --help

argparse erzeugt automatisch eine brauchbare Hilfeseite für das Programm.


33. Logging statt print()

print() eignet sich hervorragend für kleine Tests. Bei größeren Programmen sollte für Status-, Warn- und Fehlermeldungen das Modul logging verwendet werden.

Logging konfigurieren

import logging


logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s"
)

logging.info("Programm gestartet")
logging.warning("Warnung")
logging.error("Fehler")

Log-Level

  • DEBUG – detaillierte Entwicklungsinformationen
  • INFO – normale Statusinformationen
  • WARNING – ungewöhnliche Situation
  • ERROR – Fehler
  • CRITICAL – schwerwiegender Fehler

In Datei protokollieren

logging.basicConfig(
    filename="programm.log",
    level=logging.INFO,
    encoding="utf-8"
)

34. Automatisierte Tests

Tests prüfen automatisch, ob Funktionen die erwarteten Ergebnisse liefern. Schon wenige Tests können verhindern, dass eine spätere Änderung unbemerkt bestehende Funktionen zerstört.

Einfache Prüfung mit assert

def addiere(a, b):
    return a + b


assert addiere(2, 3) == 5
assert addiere(-1, 1) == 0

unittest

import unittest


def addiere(a, b):
    return a + b


class TestBerechnungen(unittest.TestCase):

    def test_addiere(self):
        self.assertEqual(
            addiere(2, 3),
            5
        )


if __name__ == "__main__":
    unittest.main()

Was sollte getestet werden?

  • normale Eingaben
  • Grenzfälle
  • leere Eingaben
  • ungültige Eingaben
  • bekannte Fehlerfälle

35. Debugging

print()-Debugging

print("Variable:", wert)
print("Typ:", type(wert))

Für kleine Skripte ist das vollkommen legitim und oft der schnellste Weg.

Debugger verwenden

Entwicklungsumgebungen wie Visual Studio Code erlauben Breakpoints. Das Programm hält an einer bestimmten Zeile an und Variablen können während der Ausführung untersucht werden.

breakpoint()

wert = berechne_etwas()

breakpoint()

print(wert)

Python besitzt einen eingebauten Einstiegspunkt für interaktives Debugging.

Fehler systematisch eingrenzen

  1. Traceback vollständig lesen.
  2. Die tatsächlich fehlerhafte Zeile bestimmen.
  3. Eingabewerte kontrollieren.
  4. Datentypen prüfen.
  5. Problem auf möglichst kleinen Code reduzieren.
  6. Erst dann umbauen.

36. Python-Programme als EXE verteilen

Python-Skripte können beispielsweise mit PyInstaller in eine unter Windows ausführbare Datei verpackt werden. Dabei wird nicht der Python-Code in klassischen Maschinencode übersetzt; vielmehr werden Interpreter, Programm und benötigte Bestandteile zu einem Paket gebündelt.

PyInstaller installieren

python -m pip install pyinstaller

Programm erstellen

pyinstaller programm.py

Eine einzelne EXE

pyinstaller --onefile programm.py

Ohne Konsolenfenster

pyinstaller --onefile --windowed programm.py

--windowed sollte nur verwendet werden, wenn das Programm tatsächlich keine Konsole benötigt. Andernfalls verschwinden Fehlermeldungen scheinbar im Nirwana.

Typische Falle beim Doppelklick

Wird eine EXE per Doppelklick gestartet, kann ihr Arbeitsverzeichnis anders sein, als man beim Start aus Visual Studio Code erwartet.

Statt blind mit relativen Pfaden zu arbeiten, ist es oft sinnvoll, den gewünschten Basisordner ausdrücklich festzulegen.

from pathlib import Path

basisordner = Path(__file__).resolve().parent

Konsole offenhalten

Während der Fehlersuche kann am Ende eines kleinen Werkzeugs vorübergehend stehen:

input("Enter drücken zum Beenden...")

Das ist keine elegante Fehlerbehandlung, verhindert beim Testen per Doppelklick aber, dass das Konsolenfenster sofort wieder verschwindet.


37. Codequalität und Stil

Sprechende Namen

# Schlecht:
x = 21.5
d = "messung.csv"

# Besser:
temperatur_celsius = 21.5
eingabedatei = "messung.csv"

Funktionen klein halten

Eine Funktion sollte möglichst eine klar umrissene Aufgabe erfüllen. Kann ihre Tätigkeit nicht in einem kurzen Satz beschrieben werden, übernimmt sie möglicherweise zu viele Aufgaben.

Wiederholungen vermeiden

Wenn derselbe Code mehrfach kopiert wird, sollte geprüft werden, ob daraus eine Funktion entstehen kann.

Magic Numbers vermeiden

# Weniger gut:
if temperatur > 80:
    alarm()

# Verständlicher:
MAXIMALE_TEMPERATUR = 80

if temperatur > MAXIMALE_TEMPERATUR:
    alarm()

PEP 8

PEP 8 ist der verbreitete Stil-Leitfaden für Python-Code. Zu den bekannten Konventionen gehören:

  • vier Leerzeichen pro Einrückung
  • snake_case für Funktionen und Variablen
  • PascalCase für Klassen
  • Leerzeichen rund um Operatoren
  • überschaubare Zeilenlängen
  • Imports am Anfang einer Datei

Lesbarkeit schlägt Cleverness

Drei zusätzliche verständliche Zeilen sind fast immer besser als ein brillanter Einzeiler, dessen Bedeutung am nächsten Dienstag niemand mehr rekonstruieren möchte.


38. Typische Fehler und Fallen

= und == verwechseln

x = 5       # Zuweisung
x == 5      # Vergleich

Index beginnt bei 0

farben = ["rot", "grün", "blau"]

print(farben[0])  # rot

Off-by-one bei range()

for zahl in range(1, 5):
    print(zahl)

Ergebnis:

1
2
3
4

Der Endwert 5 gehört nicht mehr zum Bereich.

String und Zahl vermischen

alter = 28

# Fehler:
# print("Alter: " + alter)

# Richtig:
print("Alter: " + str(alter))

# Noch besser:
print(f"Alter: {alter}")

input() liefert immer einen String

alter = input("Alter: ")

print(type(alter))

Für Berechnungen daher beispielsweise:

alter = int(input("Alter: "))

Listen nicht unbeabsichtigt teilen

a = [1, 2, 3]
b = a

b.append(4)

print(a)

a und b verweisen hier auf dieselbe Liste.

Für eine flache Kopie:

b = a.copy()

Veränderbare Standardargumente vermeiden

# Problematisch:
def hinzufuegen(wert, liste=[]):
    liste.append(wert)
    return liste

Besser:

def hinzufuegen(wert, liste=None):
    if liste is None:
        liste = []

    liste.append(wert)

    return liste

Dateipfade nicht blind zusammenbauen

# Eher vermeiden:
pfad = "daten/" + dateiname

# Besser:
from pathlib import Path

pfad = Path("daten") / dateiname

Exceptions nicht verschlucken

# Schlecht:
try:
    etwas()
except:
    pass

Damit verschwinden unter Umständen echte Programmfehler völlig lautlos.

Keine eingebauten Namen überschreiben

# Ungünstig:
list = [1, 2, 3]
str = "Hallo"
file = "daten.txt"

Dadurch überschreibt man Namen eingebauter Python-Funktionen oder etabliert unnötig verwirrende Bezeichnungen.


39. Sinnvolle Projektstruktur

Sobald ein Python-Projekt über ein einzelnes Skript hinauswächst, sollte der Quellcode sinnvoll aufgeteilt werden.

Kleines Projekt

mein_projekt/
│
├── main.py
├── daten.py
├── utils.py
├── requirements.txt
├── README.md
├── .gitignore
│
├── daten/
│   └── messungen.csv
│
└── tests/
    └── test_daten.py

Aufgaben trennen

Beispielsweise:

  • main.py – Programmeinstieg
  • daten.py – Einlesen und Speichern
  • sensor.py – Sensorlogik
  • utils.py – Hilfsfunktionen
  • tests/ – automatisierte Tests

.gitignore

Typische Einträge:

.venv/
__pycache__/
*.pyc
dist/
build/
*.spec

40. Praktische Regeln für Python-Projekte


41. Ein vollständiges kleines Beispiel

Das folgende Beispiel verbindet mehrere der zuvor behandelten Konzepte: Funktionen, pathlib, CSV-Verarbeitung, Fehlerbehandlung, Logging und einen sauberen Programmeinstieg.

import csv
import logging
from pathlib import Path


logging.basicConfig(
    level=logging.INFO,
    format="%(levelname)s: %(message)s"
)


def finde_csv_dateien(ordner: Path) -> list[Path]:
    """Findet alle CSV-Dateien im angegebenen Verzeichnis."""

    return sorted(ordner.glob("*.csv"))


def zeige_dateien(dateien: list[Path]) -> None:
    """Gibt die gefundenen Dateien aus."""

    for nummer, datei in enumerate(dateien, start=1):
        print(f"{nummer}: {datei.name}")


def zaehle_zeilen(datei: Path) -> int:
    """Zählt die Datenzeilen einer CSV-Datei."""

    with datei.open(
        "r",
        encoding="utf-8",
        newline=""
    ) as csv_datei:

        reader = csv.reader(csv_datei)

        # Kopfzeile überspringen
        next(reader, None)

        return sum(1 for _ in reader)


def main() -> None:

    ordner = Path(__file__).resolve().parent

    logging.info(
        "Suche CSV-Dateien in %s",
        ordner
    )

    dateien = finde_csv_dateien(ordner)

    if not dateien:
        logging.warning(
            "Keine CSV-Dateien gefunden."
        )
        return

    zeige_dateien(dateien)

    for datei in dateien:

        try:
            anzahl = zaehle_zeilen(datei)

        except (OSError, UnicodeError) as fehler:
            logging.error(
                "Fehler bei %s: %s",
                datei.name,
                fehler
            )
            continue

        print(
            f"{datei.name}: "
            f"{anzahl} Datenzeilen"
        )


if __name__ == "__main__":
    main()

Bereits dieses kleine Beispiel zeigt einen wichtigen Unterschied zwischen bloß „funktionierendem Code“ und brauchbarem Programmcode: Die einzelnen Aufgaben sind getrennt, Fehler werden kontrolliert behandelt, Pfade werden sauber bestimmt und der Programmablauf bleibt nachvollziehbar.


42. Was man als Nächstes lernen sollte

Wer die Inhalte dieser Seite beherrscht, besitzt bereits ein solides Fundament. Ab diesem Punkt hängt der weitere Lernweg vor allem davon ab, wofür Python eingesetzt werden soll.

Automatisierung

  • pathlib
  • shutil
  • subprocess
  • CSV und JSON
  • Excel-Dateien
  • Datei- und Ordnerverarbeitung

Raspberry Pi und Elektronik

  • GPIO
  • I²C
  • SPI
  • UART
  • Sensorbibliotheken
  • serielle Kommunikation
  • MQTT

Datenanalyse

  • NumPy
  • pandas
  • Matplotlib
  • Jupyter
  • Statistik

Webentwicklung

  • HTTP-Grundlagen
  • REST-APIs
  • Flask
  • FastAPI
  • Django
  • SQL

Professionellere Softwareentwicklung

  • Git
  • Tests
  • Type Checking
  • Linting
  • Packaging
  • Design Patterns
  • Continuous Integration

Netzwerk und Schnittstellen

  • Sockets
  • HTTP
  • APIs
  • SSH-Automatisierung
  • MQTT
  • WebSockets

43. Die wichtigsten Python-Muster auf einen Blick

# Ausgabe
print("Hallo")


# Variable
name = "Max"


# Bedingung
if name == "Max":
    print("Gefunden")


# Liste
werte = [1, 2, 3]


# Schleife
for wert in werte:
    print(wert)


# Funktion
def verdoppeln(wert):
    return wert * 2


# Dictionary
person = {
    "name": "Max",
    "alter": 28
}


# Fehlerbehandlung
try:
    zahl = int("42")
except ValueError:
    print("Fehler")


# Datei lesen
with open(
    "daten.txt",
    encoding="utf-8"
) as datei:
    text = datei.read()


# Pfade
from pathlib import Path

ordner = Path(__file__).resolve().parent


# Dateien suchen
dateien = sorted(
    ordner.glob("*.csv")
)


# sauberer Programmeinstieg
def main():
    print("Start")


if __name__ == "__main__":
    main()

Python lernt man nicht dadurch, dass man jede Methode auswendig kennt. Entscheidend ist, die grundlegenden Konzepte zu verstehen und zu wissen, welche Werkzeuge existieren. Den exakten Funktionsnamen kann man nachschlagen; ein sauberes gedankliches Modell des Programms hingegen nicht.

↑ nach oben


↑ top⌂ home