4. Mehr Werkzeuge zur Ablaufsteuerung
*************************************

Neben der eben vorgestellten "while"-Anweisung verwendet Python noch
ein paar weitere, die uns in diesem Kapitel begegnen werden.


4.1. "if"-Anweisungen
=====================

Die wohl bekannteste Anweisungsart ist die "if"-Anweisung. Zum
Beispiel:

   x = int(input("Bitte gib eine ganzzahlige Zahl ein: "))
   Bitte gib eine ganzzahlige Zahl ein: 42
   if x < 0:
   ...     x = 0
   ...     print('Negativ in Null geändert')
   ... elif x == 0:
   ...     print('Null')
   ... elif x == 1:
   ...     print('Eins')
   ... else:
   ...     print('Mehr')
   ...
   Mehr

Es kann kein oder mehrere "elif"-Teile geben, und der "else"-Teil ist
optional. Das Schlüsselwort '"elif"' ist die Abkürzung für 'else if'
und ist nützlich, um übermäßige Einrückungen zu vermeiden. Eine "if"
... "elif" ... "elif" ...-Sequenz ersetzt die *switch*- oder
*case*-Anweisungen, die man aus anderen Sprachen kennt.

Wenn du denselben Wert mit mehreren Konstanten vergleichst oder auf
bestimmte Typen oder Attribute prüfst, findest du möglicherweise auch
die "match"-Anweisung nützlich. Für weitere Details siehe match-
Anweisungen.


4.2. "for"-Anweisungen
======================

Die "for"-Anweisung in Python unterscheidet sich ein wenig von dem,
was du vielleicht aus C oder Pascal gewohnt bist. Anstatt immer über
eine arithmetische Zahlenfolge zu iterieren (wie in Pascal) oder der
nutzenden Person die Möglichkeit zu geben, sowohl den
Iterationsschritt als auch die Abbruchbedingung zu definieren (wie in
C), iteriert Pythons "for"-Anweisung über die Elemente einer
beliebigen Sequenz (eine Liste oder eine Zeichenkette) in der
Reihenfolge, in der sie in der Sequenz erscheinen. Zum Beispiel (ohne
Wortspiel):

   >>> # Einige Zeichenketten messen:
   >>> words = ['cat', 'window', 'defenestrate']
   >>> for w in words:
   ...     print(w, len(w))
   ...
   cat 3
   window 6
   defenestrate 12

Code, der eine Sammlung verändert, während über dieselbe Sammlung
iteriert wird, kann knifflig sein. Stattdessen ist es gewöhnlich
einfacher, über eine Kopie der Sammlung zu iterieren oder eine neue
Sammlung zu erstellen:

   # Eine Beispiel-Sammlung erstellen
   users = {'Hans': 'active', 'Éléonore': 'inactive', '景太郎': 'active'}

   # Strategie: Über eine Kopie iterieren
   for user, status in users.copy().items():
       if status == 'inactive':
           del users[user]

   # Strategie: Eine neue Sammlung erstellen
   active_users = {}
   for user, status in users.items():
       if status == 'active':
           active_users[user] = status


4.3. Die Funktion "range()"
===========================

Wenn du über eine Zahlenfolge iterieren musst, ist die eingebaute
Funktion "range()" praktisches Hilfsmittel. Sie erzeugt arithmetische
Folgen:

   >>> for i in range(5):
   ...     print(i)
   ...
   0
   1
   2
   3
   4

Der angegebene Endpunkt ist nie Teil der erzeugten Sequenz;
"range(10)" erzeugt 10 Werte, welche genau den gültigen Indizes für
die Elemente einer Sequenz der Länge 10 entsprechen. Es ist möglich,
die Spanne bei einer anderen Zahl beginnen zu lassen oder einen
anderen Schrittabstand anzugeben (sogar negativ; manchmal wird dies
als ‚Schrittweite‘ bezeichnet):

   >>> list(range(5, 10))
   [5, 6, 7, 8, 9]

   >>> list(range(0, 10, 3))
   [0, 3, 6, 9]

   >>> list(range(-10, -100, -30))
   [-10, -40, -70]

Um über die Indizes einer Sequenz zu iterieren, kannst du "range()"
und "len()" wie folgt kombinieren:

   >>> a = ['Mary', 'had', 'a', 'little', 'lamb']
   >>> for i in range(len(a)):
   ...     print(i, a[i])
   ...
   0 Mary
   1 had
   2 a
   3 little
   4 lamb

In den meisten dieser Fälle ist es jedoch praktischer, die Funktion
"enumerate()" zu verwenden, siehe Schleifentechniken.

Etwas Merkwürdiges passiert, wenn du ein range einfach ausgibst:

   >>> range(10)
   range(0, 10)

In vielerlei Hinsicht verhält sich das von "range()" zurückgegebene
Objekt wie eine Liste, aber das ist es in Wahrheit nicht. Es ist ein
Objekt, das die aufeinanderfolgenden Elemente der gewünschten Sequenz
zurückgibt, wenn man darüber iteriert, aber es erstellt die Liste
nicht wirklich und spart so Speicherplatz.

We say such an object is *iterable*, that is, suitable as a target for
functions and constructs that expect something from which they can
obtain successive items until the supply is exhausted.  We have seen
that the "for" statement is such a construct, while an example of a
function that takes an iterable is "sum()":

   >>> sum(range(4))  # 0 + 1 + 2 + 3
   6

Später werden wir weitere Funktionen sehen, die Iterables zurückgeben
und Iterables als Argumente entgegennehmen. Im Kapitel Datenstrukturen
werden wir genauer über "list()" sprechen.


4.4. "break"- und "continue"-Anweisungen
========================================

Die "break"-Anweisung bricht aus der innersten umschließenden "for"-
oder "while"-Schleife aus:

   >>> for n in range(2, 10):
   ...     for x in range(2, n):
   ...         if n % x == 0:
   ...             print(f"{n} equals {x} * {n//x}")
   ...             break
   ...
   4 equals 2 * 2
   6 equals 2 * 3
   8 equals 2 * 4
   9 equals 3 * 3

Die "continue"-Anweisung setzt die Ausführung mit der nächsten
Iteration der Schleife fort:

   ...     print(f"Found an odd number {num}")
   ...
   Found an even number 2
   Found an odd number 3
   Found an even number 4
   Found an odd number 5
   Found an even number 6
   Found an odd number 7
   Found an even number 8
   Found an odd number 9


4.5. "else"-Klauseln bei Schleifen
==================================

In einer "for"- oder "while"-Schleife kann die "break"-Anweisung mit
einer "else"-Klausel kombiniert werden. Wenn die Schleife beendet
wird, ohne dass "break" ausgeführt wurde, wird die "else"-Klausel
ausgeführt.

In einer "for"-Schleife wird die "else"-Klausel ausgeführt, nachdem
die Schleife ihre letzte Iteration beendet hat, also wenn kein break
aufgetreten ist.

In einer "while"-Schleife wird sie ausgeführt, nachdem die Bedingung
der Schleife falsch wird.

In beiden Schleifenarten wird die "else"-Klausel **nicht** ausgeführt,
wenn die Schleife durch ein "break" abgebrochen wurde. Andere Arten,
die Schleife vorzeitig zu beenden, wie etwa ein "return" oder eine
ausgelöste Ausnahme (Exception), überspringen die Ausführung der
"else"-Klausel natürlich ebenfalls.

Dies wird in der folgenden "for"-Schleife veranschaulicht, die nach
Primzahlen sucht:

   ...         print(n, 'is a prime number')
   ...
   2 is a prime number
   3 is a prime number
   4 equals 2 * 2
   5 is a prime number
   6 equals 2 * 3
   7 is a prime number
   8 equals 2 * 4
   9 equals 3 * 3

(Ja, das ist der korrekte Code. Sieh genau hin: Die "else"-Klausel
gehört zur "for"-Schleife, **nicht** zur "if"-Anweisung.)

Eine Möglichkeit, sich die else-Klausel vorzustellen, besteht darin,
sie gedanklich mit dem "if" innerhalb der Schleife zu koppeln. Wenn
die Schleife ausgeführt wird, durchläuft sie eine Sequenz wie
if/if/if/else. Das "if" befindet sich innerhalb der Schleife und wird
mehrmals angetroffen. Wenn die Bedingung irgendwann wahr ist, erfolgt
ein "break". Wenn die Bedingung nie wahr ist, wird die "else"-Klausel
außerhalb der Schleife ausgeführt.

Wenn sie mit einer Schleife verwendet wird, hat die "else"-Klausel
mehr mit der "else"-Klausel einer "try"-Anweisung gemeinsam als mit
der von "if"-Anweisungen: Die "else"-Klausel einer "try"-Anweisung
wird ausgeführt, wenn keine Ausnahme auftritt, und die "else"-Klausel
einer Schleife wird ausgeführt, wenn kein "break" auftritt. Für
weitere Informationen zur "try"-Anweisung und zu Ausnahmen siehe
Behandlung von Ausnahmen.


4.6. "pass"-Anweisungen
=======================

Die "pass"-Anweisung tut nichts. Sie kann verwendet werden, wenn eine
Anweisung syntaktisch erforderlich ist, das Programm aber keine Aktion
erfordert. Zum Beispiel:

   >>> while True:
   ...     pass  # Warten auf Tastaturunterbrechung (Strg+C)
   ...

Dies wird häufig verwendet, um minimale Klassen zu erstellen:

   >>> class MyEmptyClass:
   ...     pass
   ...

Ein weiterer Ort, an dem "pass" verwendet werden kann, ist als
Platzhalter für einen Funktionsrumpf oder einen bedingten Zweig, wenn
Sie an neuem Code arbeiten. So können Sie weiterhin auf einer
abstrakteren Ebene denken. Das "pass" wird stillschweigend ignoriert:

   >>> def initlog(*args):
   ...     pass   # Denken Sie daran, dies zu implementieren!
   ...

Für diesen letzten Fall verwenden viele Leute das Ellipsis-Literal
"..." anstelle von "pass". Diese Verwendung hat für Python keine
besondere Bedeutung und ist kein Teil der Sprachdefinition (Sie
könnten hier jeden konstanten Ausdruck verwenden), aber "..." wird
konventionell ebenfalls als Platzhalter für einen Rumpf verwendet.
Siehe Das Ellipsis-Objekt.


4.7. "match"-Anweisungen
========================

A "match" statement takes an expression and compares its value to
successive patterns given as one or more case blocks.  This is
superficially similar to a switch statement in C, Java or JavaScript
(and many other languages), but it's more similar to pattern matching
in languages like Rust or Haskell. Only the first pattern that matches
gets executed and it can also extract components (sequence elements or
object attributes) from the value into variables.

Die einfachste Form vergleicht einen Subjektwert mit einem oder
mehreren Literalen:

   def http_error(status):
       match status:
           case 400:
               return "Bad request"
           case 404:
               return "Not found"
           case 418:
               return "I'm a teapot"
           case _:
               return "Something's wrong with the internet"

Note the last block: the "variable name" "_" acts as a *wildcard* and
never fails to match. If no case matches, none of the branches is
executed.

Sie können mehrere Literale in einem einzigen Muster mithilfe von "|"
("oder") kombinieren:

   case 401 | 403 | 404:
       return "Not allowed"

Muster können wie Entpackungs-Zuweisungen aussehen und verwendet
werden, um Variablen zu binden:

   # point ist ein (x, y)-Tupel
   match point:
       case (0, 0):
           print("Origin")
       case (0, y):
           print(f"Y={y}")
       case (x, 0):
           print(f"X={x}")
       case (x, y):
           print(f"X={x}, Y={y}")
       case _:
           raise ValueError("Not a point")

Schauen Sie sich das genau an! Das erste Muster hat zwei Literale und
kann als Erweiterung des oben gezeigten Literal-Musters betrachtet
werden. Die nächsten beiden Muster kombinieren jedoch ein Literal und
eine Variable, und die Variable *bindet* einen Wert aus dem Subjekt
("point"). Das vierte Muster erfasst zwei Werte, was es konzeptionell
ähnlich wie das Entpacken einer Zuweisung "(x, y) = point" macht.

Wenn Sie Klassen zur Strukturierung Ihrer Daten verwenden, können Sie
den Klassennamen gefolgt von einer Argumentliste verwenden, die einem
Konstruktor ähneln, jedoch die Möglichkeit bieten, Attribute in
Variablen zu erfassen:

   class Point:
       def __init__(self, x, y):
           self.x = x
           self.y = y

   def where_is(point):
       match point:
           case Point(x=0, y=0):
               print("Origin")
           case Point(x=0, y=y):
               print(f"Y={y}")
           case Point(x=x, y=0):
               print(f"X={x}")
           case Point():
               print("Somewhere else")
           case _:
               print("Not a point")

Sie können Positions-Parameter mit einigen eingebauten Klassen
verwenden, die eine Reihenfolge für ihre Attribute festlegen (z. B.
dataclasses). Sie können auch eine spezifische Position für Attribute
in Mustern definieren, indem Sie das spezielle Attribut
"__match_args__" in Ihren Klassen setzen. Wenn es auf ("x", "y")
gesetzt ist, sind die folgenden Muster alle äquivalent (und binden
alle das Attribut "y" an die Variable "var"):

   Point(1, var)
   Point(1, y=var)
   Point(x=1, y=var)
   Point(y=var, x=1)

Eine empfohlene Art, Muster zu lesen, besteht darin, sie als eine
erweiterte Form dessen zu betrachten, was Sie auf die linke Seite
einer Zuweisung setzen würden, um zu verstehen, welche Variablen auf
was gesetzt werden. Nur den einzeln stehenden Namen (wie "var" oben)
wird von einer match-Anweisung etwas zugewiesen. Namen mit Punkten
(wie "foo.bar"), Attributnamen (die "x=" und "y=" oben) oder
Klassennamen (erkennbar am ""(...)" daneben wie "Point" oben) wird
niemals etwas zugewiesen.

Muster können beliebig verschachtelt werden. Wenn wir beispielsweise
eine kurze Liste von Point-Objekten haben, denen "__match_args__"
hinzugefügt wurde, könnten wir sie wie folgt abgleichen:

   class Point:
       __match_args__ = ('x', 'y')
       def __init__(self, x, y):
           self.x = x
           self.y = y

   match points:
       case []:
           print("No points")
       case [Point(0, 0)]:
           print("The origin")
       case [Point(x, y)]:
           print(f"Single point {x}, {y}")
       case [Point(0, y1), Point(0, y2)]:
           print(f"Two on the Y axis at {y1}, {y2}")
       case _:
           print("Something else")

Wir können einem Muster eine "if"-Klausel hinzufügen, die als
"Guard(Wächter) bezeichnet wird. Wenn der Guard nicht zutrifft, fährt
"match" mit dem nächsten case-Block fort. Beachten Sie, dass das
Erfassen der Werte erfolgt, bevor der Guard ausgewertet wird:

   match point:
       case Point(x, y) if x == y:
           print(f"Y=X at {x}")
       case Point(x, y):
           print(f"Not on the diagonal")

Einige weitere Hauptmerkmale dieser Anweisung:

* Wie bei Zuweisungen durch Entpacken haben Tupel- und Listenmuster
  exakt dieselbe Bedeutung und passen tatsächlich auf beliebige
  Sequenzen. Eine wichtige Ausnahme ist, dass sie nicht auf Iteratoren
  oder Zeichenketten passen.

* Sequenzmuster unterstützen erweitertes Entpacken: "[x, y, *rest]"
  und "(x, y, *rest)" funktionieren ähnlich wie Zuweisungen durch
  Entpacken. Der Name nach "*" darf auch "_" sein, sodass "(x, y, *_)"
  auf eine Sequenz von mindestens zwei Elementen passt, ohne die
  verbleibenden Elemente zu binden.

* Mapping-Muster: "{\"bandwidth\": b, \"latency\": l}" fängt die Werte
  "\"bandwidth\"" und "\"latency\"" aus einem Dictionary ab. Anders
  als bei Sequenzmustern werden zusätzliche Schlüssel ignoriert. Ein
  Entpacken wie "**rest" wird ebenfalls unterstützt. (Aber "**_" wäre
  redundant und ist daher nicht erlaubt.)

* Untermuster können mit dem Schlüsselwort "as" abgefangen werden:

     case (Point(x1, y1), Point(x2, y2) as p2): ...

  fängt das zweite Element der Eingabe als "p2" ab (solange die
  Eingabe eine Sequenz aus zwei Punkten ist)

* Die meisten Literale werden auf Gleichheit verglichen, die
  Singletons "True", "False" und "None" jedoch auf Identität.

* Muster dürfen benannte Konstanten verwenden. Diese müssen punktierte
  Namen sein, um zu verhindern, dass sie als Erfassungsvariable
  interpretiert werden:

     from enum import Enum
     class Color(Enum):
         RED = 'red'
         GREEN = 'green'
         BLUE = 'blue'

     color = Color(input("Enter your choice of 'red', 'blue' or 'green': "))

     match color:
         case Color.RED:
             print("I see red!")
         case Color.GREEN:
             print("Grass is green")
         case Color.BLUE:
             print("I'm feeling the blues :(")

Für eine ausführlichere Erklärung und weitere Beispiele können Sie
einen Blick in **PEP 636** werfen, das im Tutorial-Format verfasst
ist.


4.8. Funktionen definieren
==========================

Wir können eine Funktion erstellen, die die Fibonacci-Folge bis zu
einer beliebigen Grenze ausgibt:

   >>> def fib(n):    # Fibonacci-Folge kleiner als n ausgeben
   ...     """Fibonacci-Folge kleiner als n ausgeben."""
   ...     a, b = 0, 1
   ...     while a < n:
   ...         print(a, end=' ')
   ...         a, b = b, a+b
   ...     print()
   ...
   >>> # Die eben definierte Funktion aufrufen:
   >>> fib(2000)
   0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

Das Schlüsselwort "def" leitet eine Funktions*definition* ein. Darauf
müssen der Funktionsname und die in Klammern gesetzte Liste der
formalen Parameter folgen. Die Anweisungen, die den Funktionskörper
bilden, beginnen in der nächsten Zeile und müssen eingerückt sein.

Die erste Anweisung des Funktionskörpers kann optional ein
Zeichenketten-Literal sein. Dieses Zeichenketten-Literal ist der
Dokumentationsstring der Funktion, oder *Docstring*. (Mehr über
Docstrings findet sich im Abschnitt Dokumentations-Zeichenketten.) Es
gibt Werkzeuge, die Docstrings verwenden, um automatisch Online- oder
gedruckte Dokumentation zu erzeugen oder es dem Benutzer ermöglichen,
interaktiv durch den Code zu navigieren; es gehört zum guten Stil,
Docstrings in geschriebenen Code einzufügen, gewöhnen Sie es sich also
am besten an.

Die *Ausführung* einer Funktion führt eine neue Symboltabelle ein, die
für die lokalen Variablen der Funktion verwendet wird. Genauer gesagt
speichern alle Variablenzuweisungen in einer Funktion den Wert in der
lokalen Symboltabelle; wohingegen Variablenreferenzen zuerst in der
lokalen Symboltabelle suchen, dann in den lokalen Symboltabellen
umschließender Funktionen, danach in der globalen Symboltabelle und
schließlich in der Tabelle der eingebauten Namen. Daher kann globalen
Variablen und Variablen umschließender Funktionen innerhalb einer
Funktion nicht direkt ein Wert zugewiesen werden (außer wenn globale
Variablen in einer "global"-Anweisung oder Variablen umschließender
Funktionen in einer "nonlocal"-Anweisung benannt sind), obwohl auf sie
verwiesen werden darf.

Die tatsächlichen Parameter (Argumente) eines Funktionsaufrufs werden
beim Aufruf in die lokale Symboltabelle der aufgerufenen Funktion
eingefügt. Daher werden Argumente per *Call by Value* übergeben (wobei
der *Wert* immer eine Objekt*referenz* ist, nicht der Wert des
Objekts). [1] Wenn eine Funktion eine andere Funktion aufruft oder
sich selbst rekursiv aufruft, wird für diesen Aufruf eine neue lokale
Symboltabelle erstellt.

Eine Funktionsdefinition verknüpft den Funktionsnamen mit dem
Funktionsobjekt in der aktuellen Symboltabelle. Der Interpreter
erkennt das Objekt, auf das dieser Name zeigt, als benutzerdefinierte
Funktion an. Andere Namen können ebenfalls auf dasselbe
Funktionsobjekt zeigen und verwendet werden, um auf die Funktion
zuzugreifen:

   >>> fib
   <function fib at 10042ed0>
   >>> f = fib
   >>> f(100)
   0 1 1 2 3 5 8 13 21 34 55 89

Wenn Sie von anderen Sprachen kommen, könnten Sie einwenden, dass
"fib" keine Funktion, sondern eine Prozedur ist, da sie keinen Wert
zurückgibt. Tatsächlich geben selbst Funktionen ohne
"return"-Anweisung einen Wert zurück, wenn auch einen eher
langweiligen. Dieser Wert heißt "None" (es ist ein eingebauter Name).
Die Ausgabe des Werts "None" wird vom Interpreter normalerweise
unterdrückt, wenn er der einzige ausgegebene Wert wäre. Sie können ihn
sehen, wenn Sie es wirklich möchten, indem Sie "print()" verwenden:

   >>> fib(0)
   >>> print(fib(0))
   None

Es ist einfach, eine Funktion zu schreiben, die eine Liste der Zahlen
der Fibonacci-Folge zurückgibt, anstatt sie auszugeben:

   >>> def fib2(n):  # Fibonacci-Folge bis n zurückgeben
   ...     """Gibt eine Liste zurück, die die Fibonacci-Folge bis n enthält."""
   ...     result = []
   ...     a, b = 0, 1
   ...     while a < n:
   ...         result.append(a)    # siehe unten
   ...         a, b = b, a+b
   ...     return result
   ...
   >>> f100 = fib2(100)    # aufrufen
   >>> f100                # das Ergebnis ausgeben
   [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Dieses Beispiel demonstriert, wie gewohnt, einige neue Python-
Funktionen:

* Die "return"-Anweisung gibt einen Wert aus einer Funktion zurück.
  "return" ohne Ausdrucksargument gibt "None" zurück. Das Erreichen
  des Endes einer Funktion gibt ebenfalls "None" zurück.

* Die Anweisung "result.append(a)" ruft eine *Methode* des
  Listenobjekts "result" auf. Eine Methode ist eine Funktion, die zu
  einem Objekt »gehört« und den Namen "obj.methodenname" trägt, wobei
  "obj" ein Objekt ist (dies kann ein Ausdruck sein) und
  "methodenname" der Name einer Methode ist, die durch den Typ des
  Objekts definiert wird. Verschiedene Typen definieren
  unterschiedliche Methoden. Methoden verschiedener Typen können
  denselben Namen haben, ohne dass es zu Mehrdeutigkeiten kommt. (Es
  ist möglich, eigene Objekttypen und Methoden mittels *Klassen* zu
  definieren, siehe Klassen.) Die im Beispiel gezeigte Methode
  "append()" ist für Listenobjekte definiert; sie fügt ein neues
  Element am Ende der Liste hinzu. In diesem Beispiel ist sie
  äquivalent zu "result = result + [a]", aber effizienter.


4.9. Mehr zum Definieren von Funktionen
=======================================

Es ist auch möglich, Funktionen mit einer variablen Anzahl von
Argumenten zu definieren. Es gibt drei Formen, die kombiniert werden
können.


4.9.1. Standardwerte für Argumente
----------------------------------

Die nützlichste Form ist die Angabe eines Standardwerts für ein oder
mehrere Argumente. Dies erzeugt eine Funktion, die mit weniger
Argumenten aufgerufen werden kann, als sie definiert ist zu erlauben.
Zum Beispiel:

   def ask_ok(prompt, retries=4, reminder='Please try again!'):
       while True:
           reply = input(prompt)
           if reply in {'y', 'ye', 'yes'}:
               return True
           if reply in {'n', 'no', 'nop', 'nope'}:
               return False
           retries = retries - 1
           if retries < 0:
               raise ValueError('invalid user response')
           print(reminder)

Diese Funktion kann auf verschiedene Arten aufgerufen werden:

* nur das erforderliche Argument übergeben: "ask_ok('Do you really
  want to quit?')"

* eines der optionalen Argumente übergeben: "ask_ok('OK to overwrite
  the file?', 2)"

* oder sogar alle Argumente übergeben: "ask_ok('OK to overwrite the
  file?', 2, 'Come on, only yes or no!')"

Dieses Beispiel führt auch das Schlüsselwort "in" ein. Dies prüft, ob
eine Sequenz einen bestimmten Wert enthält oder nicht.

Die Standardwerte werden zum Zeitpunkt der Funktionsdefinition im
*definierenden* Gültigkeitsbereich ausgewertet, so dass

   i = 5

   def f(arg=i):
       print(arg)

   i = 6
   f()

wird "5" ausgeben.

**Wichtige Warnung:** Der Standardwert wird nur einmal ausgewertet.
Dies macht einen Unterschied, wenn der Standardwert ein veränderbares
Objekt wie eine Liste, ein Dictionary oder Instanzen der meisten
Klassen ist. Die folgende Funktion sammelt beispielsweise die ihr
übergebenen Argumente bei nachfolgenden Aufrufen an:

   def f(a, L=[]):
       L.append(a)
       return L

   print(f(1))
   print(f(2))
   print(f(3))

Dies gibt Folgendes aus

   [1]
   [1, 2]
   [1, 2, 3]

Wenn Sie nicht möchten, dass der Standardwert zwischen nachfolgenden
Aufrufen geteilt wird, können Sie die Funktion stattdessen so
schreiben:

   def f(a, L=None):
       if L is None:
           L = []
       L.append(a)
       return L


4.9.2. Schlüsselwortargumente
-----------------------------

Funktionen können auch mittels *Schlüsselwortargumenten* der Form
"kwarg=wert" aufgerufen werden. Die folgende Funktion beispielsweise:

   def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
       print("-- This parrot wouldn't", action, end=' ')
       print("if you put", voltage, "volts through it.")
       print("-- Lovely plumage, the", type)
       print("-- It's", state, "!")

akzeptiert ein erforderliches Argument ("voltage") und drei optionale
Argumente ("state", "action" und "type"). Diese Funktion kann auf jede
der folgenden Arten aufgerufen werden:

   parrot(1000)                                          # 1 Positionsargument
   parrot(voltage=1000)                                  # 1 Schlüsselwortargument
   parrot(voltage=1000000, action='VOOOOOM')             # 2 Schlüsselwortargumente
   parrot(action='VOOOOOM', voltage=1000000)             # 2 Schlüsselwortargumente
   parrot('a million', 'bereft of life', 'jump')         # 3 Positionsargumente
   parrot('a thousand', state='pushing up the daisies')  # 1 Positionsargument, 1 Schlüsselwortargument

aber alle folgenden Aufrufe wären ungültig:

   parrot()                     # erforderliches Argument fehlt
   parrot(voltage=5.0, 'dead')  # Nicht-Schlüsselwortargument nach einem Schlüsselwortargument
   parrot(110, voltage=220)     # doppelter Wert für dasselbe Argument
   parrot(actor='John Cleese')  # unbekanntes Schlüsselwortargument

In einem Funktionsaufruf müssen Schlüsselwortargumente auf
Positionsargumente folgen. Alle übergebenen Schlüsselwortargumente
müssen mit einem der von der Funktion akzeptierten Argumente
übereinstimmen (z. B. ist "actor" kein gültiges Argument für die
Funktion "parrot"), und ihre Reihenfolge ist nicht von Bedeutung. Dies
schließt auch nicht-optionale Argumente ein (z. B. ist auch
"parrot(voltage=1000)" gültig). Kein Argument darf mehr als einmal
einen Wert erhalten. Hier ist ein Beispiel, das aufgrund dieser
Einschränkung fehlschlägt:

   >>> def function(a):
   ...     pass
   ...
   >>> function(0, a=0)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: function() got multiple values for argument 'a'

Wenn ein abschließender formaler Parameter der Form "**name" vorhanden
ist, erhält dieser ein Dictionary (siehe Mapping-Typen – dict), das
alle Schlüsselwortargumente enthält, mit Ausnahme derjenigen, die
einem formalen Parameter entsprechen. Dies kann mit einem formalen
Parameter der Form "*name" kombiniert werden (beschrieben im nächsten
Unterabschnitt), welcher ein Tupel erhält, das die Positionsargumente
außerhalb der formalen Parameterliste enthält.  ("*name" muss vor
"**name" stehen.) Wenn wir beispielsweise eine Funktion wie folgt
definieren:

   def cheeseshop(kind, *arguments, **keywords):
       print("-- Do you have any", kind, "?")
       print("-- I'm sorry, we're all out of", kind)
       for arg in arguments:
           print(arg)
       print("-" * 40)
       for kw in keywords:
           print(kw, ":", keywords[kw])

Sie könnte wie folgt aufgerufen werden:

   cheeseshop("Limburger", "It's very runny, sir.",
              "It's really very, VERY runny, sir.",
              shopkeeper="Michael Palin",
              client="John Cleese",
              sketch="Cheese Shop Sketch")

und sie würde natürlich Folgendes ausgeben:

   -- Do you have any Limburger ?
   -- I'm sorry, we're all out of Limburger
   It's very runny, sir.
   It's really very, VERY runny, sir.
   ----------------------------------------
   shopkeeper : Michael Palin
   client : John Cleese
   sketch : Cheese Shop Sketch

Beachte, dass die Reihenfolge, in der die Schlüsselwortargumente
ausgegeben werden, garantiert der Reihenfolge entspricht, in der sie
im Funktionsaufruf übergeben wurden.


4.9.3. Spezielle Parameter
--------------------------

Standardmäßig können Argumente an eine Python-Funktion entweder über
die Position oder explizit als Schlüsselwort übergeben werden. Aus
Gründen der Lesbarkeit und Performance ist es sinnvoll, die Art und
Weise der Argumentübergabe einzuschränken, sodass Entwickler nur auf
die Funktionsdefinition schauen müssen, um festzustellen, ob Argumente
über die Position, über Position oder Schlüsselwort oder über
Schlüsselwörter übergeben werden.

Eine Funktionsdefinition kann wie folgt aussehen:

   def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
         -----------    ----------     ----------
           |             |                  |
           |        Positional or keyword   |
           |                                - Keyword only
            -- Positional only

wobei "/" und "*" optional sind. Wenn sie verwendet werden, geben
diese Symbole die Art des Parameters dadurch an, wie die Argumente an
die Funktion übergeben werden dürfen: Nur-Positions- (positional-
only), Positions- oder Schlüsselwort- (positional-or-keyword) und Nur-
Schlüsselwort-Parameter (keyword-only). Schlüsselwortparameter werden
auch als benannte Parameter bezeichnet.


4.9.3.1. Positions- oder Schlüsselwortargumente
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Wenn "/" und "*" in der Funktionsdefinition nicht vorhanden sind,
können Argumente an eine Funktion über die Position oder über ein
Schlüsselwort übergeben werden.


4.9.3.2. Reine Positionsargumente
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Schaut man sich das genauer an, ist es möglich, bestimmte Parameter
als *positional-only* (reine Positions-Parameter) zu kennzeichnen.
Wenn sie *positional-only* sind, spielt die Reihenfolge der Parameter
eine Rolle, und sie können nicht als Schlüsselwort übergeben werden.
Reine Positions-Parameter stehen vor einem Schrägstrich "/". Der "/"
wird verwendet, um die reinen Positions-Parameter logisch vom Rest der
Parameter zu trennen. Wenn in der Funktionsdefinition kein "/"
vorkommt, gibt es keine reinen Positions-Parameter.

Parameter, die dem "/" folgen, können *positional-or-keyword* oder
*keyword-only* sein.


4.9.3.3. eine Schlüsselwortargumente
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Um Parameter als *keyword-only* zu kennzeichnen, was anzeigt, dass die
Parameter als Schlüsselwortargument übergeben werden müssen, setze ein
"*" in die Argumentliste direkt vor den ersten *keyword-
only*-Parameter.


4.9.3.4. Funktionsbeispiele
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Betrachte die folgenden Definitionen der Beispielfunktionen und achte
dabei genau auf die Markierungen "/" und "*":

   >>> def standard_arg(arg):
   ...     print(arg)
   ...
   >>> def pos_only_arg(arg, /):
   ...     print(arg)
   ...
   >>> def kwd_only_arg(*, arg):
   ...     print(arg)
   ...
   >>> def combined_example(pos_only, /, standard, *, kwd_only):
   ...     print(pos_only, standard, kwd_only)

Die erste Funktionsdefinition, "standard_arg", die geläufigste Form,
schränkt die Aufrufkonvention nicht ein und Argumente können über die
Position oder per Schlüsselwort übergeben werden:

   >>> standard_arg(2)
   2

   >>> standard_arg(arg=2)
   2

Die zweite Funktion "pos_only_arg" ist darauf beschränkt, nur
Positionsparameter zu verwenden, da in der Funktionsdefinition ein "/"
steht:

   >>> pos_only_arg(1)
   1

   >>> pos_only_arg(arg=1)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments: 'arg'

Die dritte Funktion "kwd_only_arg" erlaubt nur Schlüsselwortargumente,
wie durch ein "*" in der Funktionsdefinition angezeigt wird:

   >>> kwd_only_arg(3)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given

   >>> kwd_only_arg(arg=3)
   3

Und die letzte verwendet alle drei Aufrufkonventionen in derselben
Funktionsdefinition:

   >>> combined_example(1, 2, 3)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: combined_example() takes 2 positional arguments but 3 were given

   >>> combined_example(1, 2, kwd_only=3)
   1 2 3

   >>> combined_example(1, standard=2, kwd_only=3)
   1 2 3

   >>> combined_example(pos_only=1, standard=2, kwd_only=3)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: combined_example() got some positional-only arguments passed as keyword arguments: 'pos_only'

Betrachte schließlich diese Funktionsdefinition, bei der es zu einer
möglichen Kollision zwischen dem Positionsargument "name" und "**kwds"
kommt, das "name" als Schlüssel enthält:

   def foo(name, **kwds):
       return 'name' in kwds

Es gibt keinen möglichen Aufruf, der dazu führt, dass sie "True"
zurückgibt, da das Schlüsselwort "'name'" immer an den ersten
Parameter gebunden wird. Zum Beispiel:

   >>> foo(1, **{'name': 2})
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: foo() got multiple values for argument 'name'
   >>>

Aber unter Verwendung von "/" (reine Positionsargumente) ist es
möglich, da dies "name" als Positionsargument und "'name'" als
Schlüssel in den Schlüsselwortargumenten erlaubt:

   >>> def foo(name, /, **kwds):
   ...     return 'name' in kwds
   ...
   >>> foo(1, **{'name': 2})
   True

Mit anderen Worten können die Namen reiner Positions-Parameter ohne
Mehrdeutigkeit in "**kwds" verwendet werden.


4.9.3.5. Rückblick
~~~~~~~~~~~~~~~~~~

Der Anwendungsfall bestimmt, welche Parameter in der
Funktionsdefinition zu verwenden sind:

   def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):

Als Orientierungshilfe:

* Verwende reine Positions-Parameter, wenn der Name der Parameter dem
  Benutzer nicht zur Verfügung stehen soll. Dies ist nützlich, wenn
  Parameternamen keine wirkliche Bedeutung haben, wenn die Reihenfolge
  der Argumente beim Aufruf der Funktion erzwungen werden soll oder
  wenn einige Positionsparameter und beliebige Schlüsselwörter
  entgegengenommen werden müssen.

* Verwende *keyword-only*, wenn Namen eine Bedeutung haben und die
  Funktionsdefinition verständlicher ist, indem Namen explizit gemacht
  werden, oder wenn du verhindern willst, dass sich Benutzer auf die
  Position des übergebenen Arguments verlassen.

* Verwende für eine API reine Positions-Parameter, um inkompatible
  API-Änderungen zu verhindern, falls der Name des Parameters in
  Zukunft geändert wird.


4.9.4. Beliebige Argumentlisten
-------------------------------

Schließlich ist die am seltensten genutzte Option anzugeben, dass eine
Funktion mit einer beliebigen Anzahl von Argumenten aufgerufen werden
kann. Diese Argumente werden in einem Tupel zusammengefasst (siehe
Tupel und Sequenzen). Vor der variablen Anzahl von Argumenten können
null oder mehr normale Argumente vorkommen.

   def write_multiple_items(file, separator, *args):
       file.write(separator.join(args))

Normalerweise stehen diese *variadischen* Argumente an letzter Stelle
in der Liste der formalen Parameter, da sie alle verbleibenden
Eingabeargumente aufsammeln, die an die Funktion übergeben werden.
Alle formalen Parameter, die nach dem Parameter "*args" stehen, sind
„Keyword-only“-Argumente, was bedeutet, dass sie nur als
Schlüsselwörter und nicht als Positionsargumente verwendet werden
können.

   >>> def concat(*args, sep="/"):
   ...     return sep.join(args)
   ...
   >>> concat("earth", "mars", "venus")
   'earth/mars/venus'
   >>> concat("earth", "mars", "venus", sep=".")
   'earth.mars.venus'


4.9.5. Entpacken von Argumentlisten
-----------------------------------

Die umgekehrte Situation tritt ein, wenn die Argumente bereits in
einer Liste oder einem Tupel vorliegen, aber für einen Funktionsaufruf
entpackt werden müssen, der separate Positionsargumente erfordert.
Beispielsweise erwartet die eingebaute "range()"-Funktion separate
*start*- und *stop*-Argumente. Stehen diese nicht separat zur
Verfügung, schreibe den Funktionsaufruf mit dem "*"\ -Operator, um die
Argumente aus einer Liste oder einem Tupel zu entpacken:

   >>> list(range(3, 6))            # Normaler Aufruf mit separaten Argumenten
   [3, 4, 5]
   >>> args = [3, 6]
   >>> list(range(*args))            # Aufruf mit aus einer Liste entpackten Argumenten
   [3, 4, 5]

Auf die gleiche Weise können Wörterbücher mit dem "**"\ -Operator
Schlüsselwortargumente übergeben:

   >>> def parrot(voltage, state='a stiff', action='voom'):
   ...     print("-- This parrot wouldn't", action, end=' ')
   ...     print("if you put", voltage, "volts through it.", end=' ')
   ...     print("E's", state, "!")
   ...
   >>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
   >>> parrot(**d)
   -- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !


4.9.6. Lambda-Ausdrücke
-----------------------

Kleine anonyme Funktionen können mit dem Schlüsselwort "lambda"
erstellt werden. Diese Funktion gibt die Summe ihrer beiden Argumente
zurück: "lambda a, b: a+b". Lambda-Funktionen können überall dort
verwendet werden, wo Funktionsobjekte benötigt werden. Sie sind
syntaktisch auf einen einzelnen Ausdruck beschränkt. Semantisch sind
sie lediglich syntaktischer Zucker für eine normale
Funktionsdefinition. Wie verschachtelte Funktionsdefinitionen können
Lambda-Funktionen auf Variablen aus dem umgebenden Gültigkeitsbereich
verweisen:

   >>> def make_incrementor(n):
   ...     return lambda x: x + n
   ...
   >>> f = make_incrementor(42)
   >>> f(0)
   42
   >>> f(1)
   43

Das obige Beispiel verwendet einen Lambda-Ausdruck, um eine Funktion
zurückzugeben. Eine weitere Anwendung ist die Übergabe einer kleinen
Funktion als Argument. Beispielsweise nimmt "list.sort()" eine
Sortierschlüsselfunktion *key* an, bei der es sich um eine Lambda-
Funktion handeln kann:

   >>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
   >>> pairs.sort(key=lambda pair: pair[1])
   >>> pairs
   [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]


4.9.7. Dokumentations-Zeichenketten
-----------------------------------

Hier sind einige Konventionen über den Inhalt und die Formatierung von
Dokumentations-Zeichenketten.

Die erste Zeile sollte immer eine kurze, prägnante Zusammenfassung des
Zwecks des Objekts sein. Der Kürze halber sollte sie weder den Namen
noch den Typ des Objekts explizit nennen, da diese auf andere Weise
verfügbar sind (außer wenn der Name zufällig ein Verb ist, das die
Funktionsweise beschreibt). Diese Zeile sollte mit einem
Großbuchstaben beginnen und mit einem Punkt enden.

Gibt es weitere Zeilen in der Dokumentations-Zeichenkette, sollte die
zweite Zeile leer sein, um die Zusammenfassung visuell vom Rest der
Beschreibung zu trennen. Die folgenden Zeilen sollten aus einem oder
mehreren Absätzen bestehen, die die Aufrufkonventionen des Objekts,
seine Seiteneffekte usw. beschreiben.

Der Python-Parser entfernt die Einrückung aus mehrzeiligen String-
Literalen, wenn diese als Modul-, Klassen- oder Funktions-Docstrings
dienen.

Hier ist ein Beispiel für einen mehrzeiligen Docstring:

   >>> def my_function():
   ...     """Tut nichts, dokumentiert dies aber.
   ...
   ...     Nein, wirklich, es tut nichts:
   ...
   ...         >>> my_function()
   ...         >>>
   ...     """
   ...     pass
   ...
   >>> print(my_function.__doc__)
   Tut nichts, dokumentiert dies aber.

   Nein, wirklich, es tut nichts:

       >>> my_function()
       >>>


4.9.8. Funktions-Annotationen
-----------------------------

Funktions-Annotationen sind völlig optionale Metadaten-Informationen
über die Typen, die von benutzerdefinierten Funktionen verwendet
werden (siehe **PEP 3107** und **PEP 484** für weitere Informationen).

*Annotations* are stored in the "__annotations__" attribute of the
function as a dictionary and have no effect on any other part of the
function.  Parameter annotations are defined by a colon after the
parameter name, followed by an expression evaluating to the value of
the annotation.  Return annotations are defined by a literal "->",
followed by an expression, between the parameter list and the colon
denoting the end of the "def" statement.  The following example has a
required argument, an optional argument, and the return value
annotated:

   >>> def f(ham: str, eggs: str = 'eggs') -> str:
   ...     print("Annotations:", f.__annotations__)
   ...     print("Arguments:", ham, eggs)
   ...     return ham + ' and ' + eggs
   ...
   >>> f('spam')
   Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>}
   Arguments: spam eggs
   'spam and eggs'


4.10. Intermezzo: Programmierstil
=================================

Nun, da du im Begriff sind, längere und komplexere Stücke Python-Code
zu schreiben, ist ein guter Zeitpunkt, über den *Programmierstil*
(*coding style*) zu sprechen. Die meisten Sprachen können in
unterschiedlichen Stilen geschrieben (oder genauer gesagt
*formatiert*) werden. Manche sind besser lesbar als andere. Es anderen
leicht zu machen, den eigenen Code zu lesen, ist immer eine gute Idee,
und die Aneignung eines guten Programmierstils hilft dabei ungemein.

Für Python hat sich **PEP 8** als der Styleguide herauskristallisiert,
an den sich die meisten Projekte halten. Er fördert einen sehr gut
lesbaren und ansprechenden Programmierstil. Alle Python-Entwickler
sollten ihn irgendwann gelesen haben. Hier sind die wichtigsten Punkte
für dich zusammengefasst:"

* Verwende 4 Leerzeichen zur Einrückung und keine Tabulatoren.

  4 Leerzeichen sind ein guter Kompromiss zwischen einer geringen
  Einrückung (ermöglicht eine größere Schachtelungstiefe) und einer
  großen Einrückung (leichter zu lesen). Tabulatoren sorgen für
  Verwirrung und werden am besten weggelassen.

* Brich Zeilen um, sodass sie 79 Zeichen nicht überschreiten.

  Dies hilft Nutzern mit kleinen Bildschirmen und ermöglicht es, auf
  größeren Bildschirmen mehrere Code-Dateien nebeneinander anzuzeigen.

* Verwende Leerzeilen, um Funktionen und Klassen sowie größere
  Codeblöcke innerhalb von Funktionen voneinander zu trennen.

* Setze Kommentare nach Möglichkeit in eine eigene Zeile.

* Verwende Dokumentations-Zeichenketten.

* Verwende Leerzeichen um Operatoren und nach Kommas, aber nicht
  direkt innerhalb von Klammerkonstrukten: "a = f(1, 2) + g(3, 4)".

* Benenne deine Klassen und Funktionen einheitlich. Die Konvention
  ist, "UpperCamelCase" für Klassen und "lowercase_with_underscores"
  für Funktionen und Methoden zu verwenden. Verwende immer "self" als
  Namen für das erste Methodenargument (siehe Ein erster Blick auf
  Klassen für mehr zu Klassen und Methoden).

* Verwende keine ausgefallenen Zeichenkodierungen, wenn dein Code in
  internationalen Umgebungen verwendet werden soll. Pythons Standard
  UTF-8 oder sogar einfaches ASCII funktionieren in jedem Fall am
  besten.

* Verwende ebenso keine Nicht-ASCII-Zeichen in Bezeichnern, wenn auch
  nur die geringste Wahrscheinlichkeit besteht, dass Personen, die
  eine andere Sprache sprechen, den Code lesen oder warten.

-[ Fußnoten ]-

[1] Tatsächlich wäre *call by object reference* (Aufruf über
    Objektreferenz) eine bessere Beschreibung, da der Aufrufer jede
    Änderung sieht, die der Aufgerufene an einem übergebenen
    veränderbaren Objekt vornimmt (z. B. in eine Liste eingefügte
    Elemente).
