Funktionen — eine Entscheidung, ein Name
Funktionen mit def schreiben, was return von print trennt, Randfälle mit einem Guard oben behandeln, und Docstrings.
Wie möchtest du lernen?
Das Problem, das wir lösen
Zwei Gruppen brauchen ihren Durchschnitt:
marks_a = [72, 45]
marks_b = [90, 33, 61]
print(round(sum(marks_a) / len(marks_a), 2))
print(round(sum(marks_b) / len(marks_b), 2))58.5
61.33Die beiden Zeilen sind fast identisch; nur der Name unterscheidet sich. Eine dritte Gruppe bedeutet eine dritte Zeile, eine vierte eine vierte.
Das Problem ist nicht das Tippen — Kopieren ist billig. Das Problem ist, dass eine Entscheidung jetzt an mehreren Stellen steht. Soll der Durchschnitt künftig auf eine Nachkommastelle statt auf zwei gerundet werden, musst du jede Zeile finden und ändern, und das Vergessen einer Zeile lässt das Programm sich auf zwei verschiedene Arten verhalten — ohne Fehler.
Und was ist mit einer leeren Liste? sum([]) / len([]) teilt durch null und das Programm stoppt. Diese Prüfung müsste an jede Zeile einzeln angehängt werden.
Eine Funktion ist ein Weg, eine Entscheidung einmal zu schreiben und ihr einen Namen zu geben. Danach erledigt der Aufruf des Namens die Arbeit, und eine Änderung bedeutet, eine Stelle zu ändern.
Am Ende dieses Kapitels kannst du
- Eine Funktion mit
defschreiben und aufrufen - Sagen, was
returnvonprinttrennt — das Wichtigste hier - Eine Funktion klein und auf einen Job beschränkt halten
- Randfälle mit einem Guard ganz oben behandeln
- Einen Docstring schreiben
Voraussetzungen: Comprehensions.
Eine erste Funktion
def average(values):
return round(sum(values) / len(values), 2)
print(average([72, 45]))
print(average([90, 33, 61]))58.5
61.33Die Form:
def— „Ich definiere eine Funktion“average— der Name, nach denselben Regeln wie ein Variablenname(values)— der Parameter, die Tür, durch die Daten hereinkommen:und ein eingerückter Block — der Körper, genau wie beiifoderforreturn— ein Ergebnis zurückgeben
average([72, 45]) zu schreiben heißt sie aufzurufen. [72, 45] ist das Argument, was tatsächlich übergeben wird; values ist der Parameter, der Name drinnen. Zwei verschiedene Wörter, und beide tauchen in Fehlermeldungen auf, daher lohnt sich die Unterscheidung.
Zwei Leerzeilen. Nach einer Funktionsdefinition zwei Leerzeilen zu lassen, ist die Python-Konvention. Python läuft auch mit weniger, aber der gesamte Python-Code wird so geschrieben, also lohnt sich die Gewohnheit.
return und print sind nicht dasselbe
Das ist der wichtigste Abschnitt im Kapitel und die größte Verwirrung bei Einsteigern.
def with_return(x):
return x * 2
def with_print(x):
print(x * 2)
a = with_return(5)
b = with_print(5)
print(a)
print(b)
print(a + 1)10
10
None
11Die ersten beiden 10 auf dem Bildschirm lassen die Funktionen identisch aussehen. Sie sind es nicht.
with_return gab den Wert zurück, also hält a die 10 und kann addiert werden — daher 11 in der letzten Zeile.
with_print zeigte den Wert an und gab nichts zurück, also ist b None. Die Zahl erschien auf dem Bildschirm, und das Programm erhielt nichts. b + 1 würde einen TypeError werfen.
Merke dir die Regel so: print ist für einen Menschen, return ist für das Programm. Eine Funktion, die rechnet, sollte return nutzen; Anzeigen passiert dort, wo sie aufgerufen wurde. So kann dieselbe Funktion heute einen Bildschirm bedienen und morgen eine Datei.
Ohne return gibt eine Funktion stillschweigend None zurück — und das ist dasselbe None, das append und .sort() in Kapitel zwölf und dreizehn zurückgaben. Jetzt siehst du, warum sie das taten: ihr Job war, etwas zu ändern, nicht etwas zurückzugeben.
return stoppt sofort
def check(mark):
if mark >= 40:
return "pass"
return "fail"
print(check(72))
print(check(20))pass
failIn dem Moment, in dem return läuft, ist die Funktion zu Ende — die Zeilen danach laufen nie. Deshalb war hier kein else nötig: wenn der erste return passiert, ist die zweite Zeile unerreichbar.
Diese Form — so früh zurückkehren, wie eine Bedingung passt — glättet die tiefen if/else-Bäume aus Kapitel neun und macht Code viel leichter lesbar.
Randfälle behandeln — ein Guard
Erinnerst du dich an das Problem, mit dem das Kapitel begann? Die leere Liste:
def average(values):
return sum(values) / len(values)
print(average([]))ZeroDivisionError: division by zeroDie Korrektur geht ganz nach oben in die Funktion:
def average(values):
if not values:
return 0.0
return round(sum(values) / len(values), 2)
print(average([72, 45]))
print(average([]))58.5
0.0if not values ist wieder die Wahrheitsregel aus Kapitel acht: eine leere Liste ist falsch, also macht not sie wahr. Kein Bedarf, len(values) == 0 zu schreiben.
Das nennt man einen Guard: den abnormalen Fall ganz oben klären, dann den Rest schreiben ohne Sorge. Funktionen machen das billig — die Prüfung wird einmal geschrieben und jeder Aufrufer profitiert.
Ist0.0die richtige Antwort? Diskutabel. Es vermischt „niemand hat eine Note“ mit „alle haben null Punkte“. Echte Programme geben oftNonezurück oder werfen einen Fehler mit Kapitel vierundzwanzigsraise.0.0bleibt hier der Einfachheit halber — aber wisse, dass es eine Entscheidung ist, keine offensichtliche Antwort.
Docstrings
Ein Stück Text in der ersten Zeile der Funktion wird zu ihrer Dokumentation:
def average(values):
"""Return the mean of values, or 0.0 when there are none."""
if not values:
return 0.0
return round(sum(values) / len(values), 2)
print(average([1, 2, 3]))
print(average.__doc__)2.0
Return the mean of values, or 0.0 when there are none.Was ihn von einem Kommentar trennt, ist, dass er ins laufende Programm überlebt — Editoren zeigen ihn an, und help(average) druckt ihn.
Ein guter Docstring sagt was zurückkommt und was an den Rändern passiert, nicht wie der Code funktioniert. Der Code steht direkt drunter.
Ein vollständiges Beispiel
report.py:
# Three small functions, each doing one thing
def average(values):
"""Return the mean of values, or 0.0 when there are none."""
if not values:
return 0.0
return round(sum(values) / len(values), 2)
def grade(mark):
"""Turn a mark into a letter."""
if mark >= 80:
return "A"
if mark >= 70:
return "B"
if mark >= 40:
return "C"
return "F"
def summarise(name, marks):
"""Build one report line for a person."""
mean = average(marks)
return f"{name:<8} {len(marks):>2} papers avg {mean:>6} grade {grade(mean)}"
people = {
"rafi": [72, 88, 61],
"ahmed": [45, 38],
"dia": [],
}
for name, marks in people.items():
print(summarise(name, marks))rafi 3 papers avg 73.67 grade B
ahmed 2 papers avg 41.5 grade C
dia 0 papers avg 0.0 grade FVier Dinge, die auffallen.
Jede Funktion macht einen Job. average bildet den Durchschnitt, grade gibt einen Buchstaben, summarise baut eine Zeile. Keine von ihnen druckt — Drucken passiert in der Schleife ganz unten, an einer Stelle. Also bedeutet das Schreiben dieses Berichts in eine Datei statt auf den Bildschirm, diese eine Zeile zu ändern.
Funktionen rufen Funktionen auf. summarise ruft average und grade auf. Das ist der eigentliche Nutzen: große Arbeit aus kleinen, verlässlichen Stücken zusammengesetzt.
grade hat kein else. Jeder return stoppt die Funktion, also bleiben die Bedingungen flach. Die Regel aus Kapitel neun gilt weiter: strengste Bedingung zuerst — 40 vor 80 zu setzen, gäbe jedem ein C.
dia's leere Liste hat nichts kaputtgemacht. Der Guard gab 0.0 zurück, grade machte daraus F, und die Berichtszeile druckte normal. Der Randfall wurde einmal, an einer Stelle behandelt.
Wenn etwas schiefgeht
NameError: name 'greet' is not defined — aber die Funktion ist direkt da Sie wurde vor ihrer def-Zeile aufgerufen. Python liest eine Datei von oben nach unten, und der Name existiert nicht, bevor das def ausgeführt wird. Halte alle defs oben und den aufrufenden Code unten.
TypeError: average() missing 1 required positional argument: 'values' Kein Argument wurde gegeben — average() statt average(marks). Die Meldung nennt sogar den fehlenden Parameter.
Die Funktion gibt None zurück Es gibt kein return, oder eines an der falschen Einrückung — innerhalb eines if, dessen Bedingung nicht passt, sagen wir. Eine andere häufige Ursache: print wurde geschrieben, wo return gemeint war.
Eine Variable drinnen in der Funktion ist draußen nicht sichtbar Das ist erwartet — warum ist Kapitel einundzwanzigs Thema. Für jetzt: return, was draußen gebraucht wird.
SyntaxError — kein Doppelpunkt in der def-Zeile Wie bei if und for endet eine def-Zeile mit einem Doppelpunkt.
IndentationError: expected an indented block after function definition Ein def braucht mindestens eine eingerückte Zeile danach.
Schritt 4 von 6 — Vorhersagen
Prüfe dein Verständnis
Die Funktion druckt statt zurückzugeben. Was geben die beiden Zeilen aus?
def double(x):
print(x * 2)
result = double(5)
print(result)- A10 None
- B10 10
- CNone 10
- D10
Zwei return und kein else. Was wird ausgegeben?
def check(mark):
if mark >= 40:
return "pass"
return "fail"
print(check(72))- Apass
- Bpass fail
- Cfail
- DNone
Die Funktion steht direkt in der Datei. Was passiert trotzdem?
print(greet("rafi"))
def greet(name):
return "hello " + name- AEin `SyntaxError`
- Bhello rafi
- CEin `TypeError`
- DEin `NameError` — die Funktion wurde aufgerufen, bevor ihre `def`-Zeile lief
Zum Antworten brauchst du ein Konto
Melde dich an, um deine Antworten zu prüfen
Die Fragen stehen oben, und sie im Kopf durchzuarbeiten ist der wichtige Teil. Melde dich an, um die Antworten, die Erklärungen und die dreistufigen Hinweise zu sehen.
Jetzt bist du dran
Schreib eine Datei namens text_tools.py mit vier Funktionen, jede mit einem Docstring, und keine von ihnen druckt etwas:
clean(text)— trimmt Leerzeichen und kleinschreibtinitials(full_name)— macht aus"Rafi Ahmed"ein"RA"word_count(text)— zählt Wörter, gibt0für einen leeren Stringsummarise(text)— ruft die anderen drei auf und gibt eine lesbare Zeile zurück
Dann unten in der Datei summarise auf ein paar Strings aufrufen und die Ergebnisse drucken — das ist die einzige Stelle, wo etwas gedruckt wird.
Zum Schluss drei Experimente:
word_count("")aufrufen. Was wäre ohne den Guard passiert?summariseseinreturndurch einprintersetzen, dann das Ergebnis in einer Variable speichern und die drucken. Was siehst du, und warum?- Den Aufruf von
cleanüber sein eigenesdefverschieben und den Fehler lesen.
Der zweite Versuch sind die wertvollsten drei Minuten in diesem Kapitel. Den Unterschied zwischen print und return einmal mit eigenen Augen zu sehen, heißt, sie nie wieder zu verwechseln.
Schritt 6 von 6
Herausforderung — das Kapitel-Quiz
Zehn Fragen von leicht bis schwer. Die letzten sind absichtlich knifflig.
Melde dich an, um das Quiz zu machen