Python-Überblick und Syntax für JS/TS-Entwickler

Track Python · PCAP-Fundament · ca. 40 Min.

Worum es geht

Die Syntax von Python ist schnell gelernt, aber an etwa einem Dutzend Stellen verhält sie sich anders als JS/TS: Blöcke per Einrückung (indentation), “falsch” ist mehr als in JS, and/or liefern Operanden statt true/false, // rundet nach unten, und Type Hints prüft zur Laufzeit niemand. Laut Glossar entstehen die meisten Anfängerfehler aus der JS-Welt bei Scope, Veränderbarkeit und Gleichheit. Die Funktions-Themen (Default-Falle, Scope, Closures) hast du schon in Lektion 01, hier geht es um den Rest der Grundsyntax, den die PCAP ständig voraussetzt.

Ablauf: erst die Übersetzungstabelle, dann fünf kleine Schritte mit lauffähigem Code, dann fünf Übungen.

Von JS/TS her gedacht

Thema JS/TS Python Stolperfalle
Blöcke { } Einrückung (4 Leerzeichen) und : falsche Einrückung ist ein IndentationError
Kein Wert null, undefined nur None prüfen mit x is None
Boolesche Werte true, false True, False Großschreibung zählt
Logik &&, \|\|, ! and, or, not liefern einen der Operanden, nicht zwingend True/False
Gleichheit === == (Wert), is (selbes Objekt) is nicht für Zahlen und Strings benutzen
Ternary a ? b : c b if a else c Reihenfolge ist anders
Länge .length len(x) Funktion statt Eigenschaft
Division / ergibt Kommazahl / ergibt immer float, // ist Ganzzahldivision -7 // 2 ist -4
Potenz ** ** ^ ist XOR
String plus Zahl "1" + 1 ergibt "11" "1" + 1 ist ein TypeError Python ist streng typisiert
Typen TS prüft beim Kompilieren Type Hints werden zur Laufzeit ignoriert die PCAP fragt Type Hints nicht ab

Der größte Gedankenwechsel in einem Bild: Dieselbe Prüfung, zwei Sprachen.

// TypeScript: der Compiler weist den Aufruf ab
function greet(name: string, times: number = 1): string {
  return ("Hallo " + name + "\n").repeat(times);
}
greet(42); // Compile-Fehler

Der Fehler kommt erst im Rumpf, bei "Hallo " + 42. Mehr dazu in Schritt 5.

Konzept in kleinen Schritten

Schritt 1: Einrückung, Kommentare, dynamisch und streng

  • Einrückung (indentation): Ein Block beginnt nach einem Doppelpunkt und endet, wenn die Einrückung zurückspringt. Üblich sind 4 Leerzeichen, Tabs und Leerzeichen nie mischen.
  • Kommentar und Docstring: # Kommentar geht bis zum Zeilenende. Ein String direkt am Anfang einer Funktion, Klasse oder Datei ist ein Docstring und über __doc__ abrufbar.
  • Dynamisch typisiert (dynamically typed): Der Wert hat den Typ, nicht die Variable. x = 1 und danach x = "a" ist erlaubt.
  • Streng typisiert (strongly typed): Python wandelt nicht heimlich um.

Es gibt weder const noch let: Eine Variable entsteht durch Zuweisung. Konstanten sind nur Konvention (MAX_SIZE).

Schritt 2: Wahrheit, Logik, Gleichheit

Truthiness: Als falsch (falsy) gelten None, False, 0, 0.0, "", [], (), {} und set(). Alles andere ist wahr. Achtung: bool("False") ist True, weil der String nicht leer ist.

and und or werten per Kurzschluss (short-circuit) aus und liefern den Operanden selbst:

Die Regel zum Merken: or gibt den ersten wahren Operanden, and den ersten falschen, und sonst jeweils den letzten.

Gleichheit: == vergleicht Werte, is fragt, ob es dasselbe Objekt ist. Nutze is praktisch nur für None. Ein Ternary schreibt sich wert_wenn_wahr if bedingung else wert_wenn_falsch. Verkettete Vergleiche (chained comparisons) sind gültig: 1 < x < 5 bedeutet 1 < x and x < 5.

Schritt 3: Arithmetik und Zuweisung

  • / liefert immer float. // rundet zum kleineren Wert ab (-7 // 2 ist -4). % ist Modulo (-7 % 2 ist 1). divmod(a, b) liefert beides als Tupel.
  • Vorrang: ** ist rechtsassoziativ (right-associative), 2 ** 3 ** 2 ist 2 ** 9. -2 ** 2 ist -4, weil ** stärker bindet als das Minus.
  • round(2.5) ergibt 2, round(3.5) ergibt 4 (zur nächsten geraden Zahl). 0.1 + 0.2 == 0.3 ist False, wie in JS.

Mehrfachzuweisung und Unpacking sparen Hilfsvariablen. Das kennst du als Destructuring:

// TypeScript
let [a, b] = [1, 2];
[a, b] = [b, a];
const [first, ...rest] = [1, 2, 3];

Schritt 4: Schleifen, print und input

  • for x in iterable, while bedingung. range(start, stop, step) hat ein exklusives Ende. enumerate(it) liefert Index und Wert, zip(a, b) läuft paarweise.
  • break beendet, continue springt zum nächsten Durchlauf, pass ist ein Platzhalter für einen leeren Block.
  • else an Schleifen: Der else-Block einer for- oder while-Schleife läuft nur, wenn die Schleife nicht mit break verlassen wurde. Das gibt es in JS nicht.

print(a, b, sep="-", end="!") steuert Trennzeichen und Zeilenende (Standard: Leerzeichen und \n). input() liefert immer einen str, für Zahlen brauchst du int(input()). input läuft im Browser nicht sinnvoll, deshalb gibt es dazu hier keine Übung. Und: int("3.5") wirft einen ValueError.

Schritt 5: Type Hints sind nur Hinweise

Type Hints (name: str, -> str) stehen in __annotations__, werden aber zur Laufzeit nicht geprüft. Das ist der größte Unterschied zu TypeScript. Prüfen musst du selbst (isinstance) oder mit einem externen Werkzeug vor dem Lauf. Die PCAP fragt Type Hints laut Glossar nicht ab.

Hier wirft nichts, weil "ab" * 2 gültig ist. Das Programm läuft mit “falschem” Typ einfach weiter.

Falle: die Prüfungs- und Praxisklassiker

  1. or und and liefern Operanden: 0 or "x" ist "x", nicht True. Und bool("False") ist True.
  2. -7 // 2 ist -4, nicht -3. // rundet nach unten, nicht zur Null.
  3. -2 ** 2 ist -4, weil ** vor dem Minus bindet.
  4. is statt ==: a is b fragt nach demselben Objekt. Nur für None verwenden.
  5. len(x) / 2 als Index: / liefert einen float, und liste[2.0] ist ein TypeError. Dafür gibt es //.
  6. Schleifen-else: gehört zur Schleife, nicht zu einem if. Es läuft, wenn die Schleife ohne break zu Ende kam (typisch: Suche ohne Treffer).
  7. Type Hints schützen nicht: greet(42) wird angenommen.

Übungen

Übung 1: Arithmetik vorhersagen

Trage ein Tupel mit den sechs Ergebnissen ein, in dieser Reihenfolge:

(2 ** 3 ** 2, -2 ** 2, -7 // 2, -7 % 2, round(2.5), 7 / 2)

Bei ** zählt, wer zuerst gerechnet wird (von rechts oder links?) und ob das Minus davor oder danach kommt. Bei // und % mit negativen Zahlen: Wohin rundet Python? Und round hat eine Besonderheit bei genau .5.

antwort = (512, -4, -4, 1, 2, 3.5)
antwort

Übung 2: Wahrheit und Logik vorhersagen

Trage wieder ein Tupel mit den sechs Ergebnissen ein. Achte genau darauf, was zurückkommt, nicht nur ob es wahr oder falsch ist:

(0 or "x", "a" and "b", "" and "b", [] or None, bool("False"), bool([0]))

Welche Operanden gelten als falsch? Dann: Wen von beiden gibt or zurück, wen and, und wann den jeweils anderen? Bei bool(...) zählt nur, ob der Container oder String leer ist, nicht sein Inhalt.

antwort = ("x", "b", "", None, True, True)
antwort

Übung 3: Schleifen-else nutzen

Die Funktion ist_prim(n) soll True liefern, wenn n eine Primzahl ist (nur durch 1 und sich selbst teilbar, die kleinste ist 2), sonst False. Fülle die Lücken. Die Schleife soll mit else arbeiten, also ohne Merker-Variable.

Das Ende von range ist exklusiv: Der letzte Teiler, den du prüfen willst, muss noch dabei sein. Und wann läuft der else-Zweig einer Schleife, in welchem Fall also hast du keinen Teiler gefunden? Prüfe dein range an den Zahlen 4 und 9.

def ist_prim(n):
    if n < 2:
        return False
    for teiler in range(2, n):
        if n % teiler == 0:
            return False
    else:
        return True

ist_prim

Übung 4: JS-Reflexe finden

Dieser Code stammt von jemandem, der JS im Kopf hat. mitte(werte) soll das mittlere Element liefern (bei gerader Länge das hintere der beiden mittleren) und bei einer leeren Liste None. Zwei Dinge sind falsch. Repariere sie.

Erstes Problem: Was fragt is eigentlich, und ist eine frisch geschriebene [] dasselbe Objekt wie deine Liste? Zweites Problem: Welchen Typ liefert / in Python, und was verlangt ein Listenindex?

def mitte(werte):
    if len(werte) == 0:
        return None
    return werte[len(werte) // 2]

mitte

Übung 5: Type Hints, was passiert wirklich?

Gegeben ist dieser Code:

def greet(name: str, times: int = 1) -> str:
    return ("Hallo " + name + "\n") * times

greet(42)

Was passiert? Trage "A", "B", "C" oder "D" ein.

  • A: Python wirft schon beim Aufruf einen TypeError, weil 42 kein str ist.
  • B: Der Aufruf läuft durch, Python macht aus 42 selbst den Text “Hallo 42”.
  • C: Der Aufruf wird angenommen, erst die Verkettung im Rumpf wirft einen TypeError.
  • D: Der Aufruf wird angenommen und liefert None, weil der Typ nicht zur Angabe passt.

Wer prüft name: str beim Aufruf? Und wenn niemand prüft: Was macht dann "Hallo " + 42 im Rumpf, und wandelt Python wie JS heimlich um?

antwort = "C"
antwort

Merksatz und Prüfstein

Merksatz: In Python entscheiden Einrückung und Wahrheitswert-Regeln über den Ablauf, and/or liefern Operanden, und Type Hints sind nur Hinweise, die zur Laufzeit niemand prüft.

Prüfstein (offene Frage): Erkläre in eigenen Worten, warum 0 or "x" den Wert "x" liefert und "" and "b" den leeren String, und warum greet(42) trotz name: str nicht sofort abgelehnt wird. Was würdest du in TypeScript an derselben Stelle bekommen?

Quelle: quellen/python-glossar-pcap-pcpp1.md, Abschnitte “Python vs. JS/TS auf einen Blick” und “Syntax und Grundlagen”. Hinweis: __annotations__, __doc__-Beispiele, .repeat und die Primzahl-Aufgabe stammen nicht aus der Quelle, wurden aber mit Python 3.13 ausgeführt (bitte prüfen, ob __annotations__ in der PCAP geprüft wird).