Du hast bereits ein Beispiel für einen Funktionsaufruf gesehen:
>>> type("32")
<type 'string'>
Der Name der aufgerufenen Funktion ist type, und sie zeigt den Typ eines Werts oder einer Variablen an. Der Wert oder die Variable, die das Argument der Funktion genannt wird, muss in runde Klammern eingeschlossen werden. Man sagt üblicherweise, die Funktion "verlangt die Übergabe eines Arguments" (oder "übernimmt ein Argument") und "gibt" ein Ergebnis "zurück". Das Ergebnis wird auch Rückgabewert genannt.
Anstatt den Rückgabewert mit print auf dem Bildschirm darzustellen, können wir ihn auch einer Variablen zuweisen:
>>> betty = type("32")
>>> print betty
<type 'string'>
Ein anderes Beispiel ist die id Funktion. Sie übernimmt einen Wert oder eine Variable als Argument und gibt eine Ganzzahl zurück, die als eindeutige Kennzahl für diesen Wert dient:
>>> id(3)
134882108
>>> betty = 3
>>> id(betty)
134882108
Jeder Wert hat eine id, d. i. eine eindeutige Zahl, die beschreibt, wo der Wert im Computer gespeichert ist. Die id einer Variablen ist die id des Wertes, auf den die Variable verweist.
Python stellt eine Sammlung von eingebauten Funktionen zur Verfügung, die Werte von einem Typ in einen anderen umwandeln. Die int Funktion übernimmt einen Wert als Argument und verwandelt ihn in eine Ganzzahl, wenn das möglich ist, oder gibt eine Fehlermeldung aus:
>>> int("32")
32
>>> int("Hello")
ValueError: invalid literal for int(): Hello
int kann auch Gleitkomma-Werte in Ganzzahlen umwandeln, aber beachte stets, dass dabei der Nachkommaanteil abgeschnitten wird:
>>> int(3.99999)
3
>>> int(-2.3)
-2
Die float Funktion wandelt Ganzzahlen und Strings in Gleitkomma-Zahlen um:
>>> float(32)
32.0
>>> float("3.14159")
3.14159
Schließlich wandelt die str Funktion in den Typ String um:
>>> str(32)
'32'
>>> str(3.14149)
'3.14149'
Es mag dir merkwürdig vorkommen, dass Python den Ganzzahl-Wert 1 vom Gleitkommawert 1.0 unterscheidet. Sie mögen zwar die selbe Zahl darstellen, aber sie gehören zu verschiedenen Typen. Der Grund dafür ist, dass sie im Computer verschieden dargestellt werden.
Nun, da wir Typen ineinander umwandeln können, haben wir eine andere Möglichkeit, mit der Ganzzahl-Division umzugehen. Kehren wir zu dem Beispiel aus dem vorigen Kapitel zurück:
Angenommen, wir möchten den Bruchteil einer Stunde berechnen, der vergangen ist. Der naheliegendste Ausdruck, minute / 60, führt Ganzzahl-Arithmetik aus, daher ist das Ergebnis immer 0, sogar 59 Minuten nach der vollen Stunde.
Eine Lösung ist, die minute in eine Gleitkommazahl zu verwandeln und damit Gleitkommadivision zu erzwingen:
>>> minute = 59
>>> float(minute) / 60
0.983333333333
Alternativ können wir den Vorteil der Regeln für die (automatische) erzwungene Typumwandlung nützen, die auf englisch type coercion) genannt wird. Für mathematische Operatoren gilt: wenn einer der beiden Operanden vom Typ float ist, dann wird der andere automatisch in den Typ float umgewandelt:
>>> minute = 59
>>> minute / 60.0
0.983333333333
Indem wir den Nenner zu einem float - Typ machen, zwingen wir Python eine Gleitkomma-Division auszuführen.
In der Mathematik hast du vielleicht Funktionen wie sin und log gesehen, und gelernt, Ausdrücke wie sin(pi/2) und log(1/x) auszuwerten. Zuerst berechnest du den Ausdruck in den Klammern (das Argument). Zum Beispiel, pi/2 ist annähernd 1.571, und 1/x ist 0.1 (wenn x zufällig 10.0 ist).
Dann wertest du die Funktion selbst aus, entweder indem du in einer Tabelle nachschaust, oder verschiedene Berechnungen ausführst. Der sin von 1.571 ist 1, und der log von 0.1 ist -1 (unter der Annahme, dass log den Logarithmus zur Basis 10 bezeichnet).
Dieser Vorgang kann wiederholt ausgeführt werden, um kompliziertere Ausdrücke auszuwerten, wie z. B. log(1/sin(pi/2)). Zuerst berechnest du das Argument der innersten Funktion, dann berechnest du den Wert der nächsten Funktion, und so weiter ...
Python hat einen math - Modul, der die meisten der uns vertrauten mathematischen Funktion zur Verfügung stellt. Ein Modul ist eine Datei, die eine ein Sammlung zu Gruppen zusammengefaßter verwandter Funktionen enthält.
Bevor wir die Funktionen aus einem Modul verwenden können, müssen wir sie importieren:
>>> import math
Um eine dieser Funktionen aufzurufen, müssen wir den Namen des Moduls angeben, gefolgt von dem Namen der Funktion. Die beiden werden durch einen Punkt getrennt. Diese Schreibweise nennt man die Punkt - Notation.
>>> decibel = math.log10(17.0)
>>> angle = 1.5
>>> height = math.sin(angle)
Die erste Anweisung setzt decibel auf den Wert Logarithmus von 17, mit Basis 10. Es gibt in dem Modul auch eine Funktion mit dem Namen log die den Logarithmus mit der Basis e bezeichnet.
Die dritte Anweisung findet den sinus des Werts der Variablen angle. sin und die anderen Trigonometrischen Funktionen (cos, tan, etc.) nehmen ihre Argumente in Radiant entgegen. Um von Grad in Radiant umzuwandeln, muß man die Gradanzahl durch 360 dividieren und mit 2*pi multiplizieren. Zum Beispiel, um den Sinus von 45 Grad zu finden, berechnen wir zuerst den Winkel im Bogenmaß und nehmen dann davon den Sinus:
>>> degrees = 45
>>> angle = degrees * 2 * math.pi / 360.0
>>> math.sin(angle)
Die Konstante pi ist auch Teil des math-Moduls. Mit einfacher Mathematik kannst du das Ergebnis kontrollieren, indem du es mít Quadratwurzel von 2 dividiert durch 2 vergleichst:
>>> math.sqrt(2) / 2.0
0.707106781187
Gerade so wie mathematische Funktionen können auch Python-Funktionen verknüpft werden, d. h. man kann einen Ausdruck als Teil von einem anderen verwenden. Zum Beispiel kann man beliebige Ausdrücke als Argument einer Funktion verwenden:
>>> x = math.cos(angle + pi/2)
Diese Anweisung dividiert den Wert von pi durch 2, und addiert das Ergebnis zu dem Wert von angle. Die Summe wird dann als Argument an die cos Function übergeben.
Man kann auch das Ergebnis eines Funktionsaufrufs als Argument an eine andere Funktion weiterreichen:
>>> x = math.exp(math.log(10.0))
Diese Anweisung berechnet den natürlichen Logarithmus von 10 (log mit Basis e) und erhebt dann e zu dieser Potenz. Das Ergebnis wird der Variablen x zugewiesen.
Bis jetzt haben wir nur Funktionen verwendet, die zu Python gehören. Es ist aber auch möglich, neue Funktionen hinzuzufügen. Das Erzeugen neuer Funktionen um spezielle Probleme zu lösen ist eine der nützlichsten Aspekte in höheren universellen Programmiersprachen.
Im Zusammenhang mit dem Programmieren ist eine Funktion eine mit Namen versehene Folge von Anweisungen, die eine gewünschte Verarbeitung von Daten ausführt. Diese Folge von Anweisungen wird in einer Funktionsdefinition festgelegt. Die Funktionen, die wir bisher benützt haben, wurden für uns definiert und diese Definitionen sind vor uns verborgen. Das ist eine gute Sache, denn es erlaubt uns, diese Funktionen zu verwenden ohne uns um die Details ihrer Definition kümmern zu müssen.
Die Syntax für eine Funktionsdefinition ist so:
def NAME( LIST OF ARAMETERLISTE ):
ANWEISUNGEN
Man kann einer Funktion, die man erzeugt jeden gewünschten, gültigen Namen geben, ausgenommen die Schlüsselwörter von Python. (siehe Kapitel 2.3.) Die Parameterliste gibt an, welche Information (falls überhaupt erforderlich) man übergeben muß, um die Funktion verwenden zu können.
Es kann eine beliebige Anzahl von Anweisungen im Inneren der Funktion (im "Funktionskörper") stehen, aber sie müssen vom linken Rand weg (alle gleich weit) eingerückt sein. In diesem Buch, werden wir eine Einrückung von zwei Leerzeichen benützen.
Die ersten paar Funktionen, die wir nun schreiben werden, haben keine Parameter. Daher sieht die Syntax so aus:
def newLine():
print
Diese Funktion heißt newLine. Die leeren runden Klammern zeigen an, dass sie keine Parameter hat. Sie enthält nur eine einzige Anweisung, die einen Zeilenvorschub auf dem Bildschirm ausgibt. (Das ist dasselbe, was geschieht, wenn man eine print-Anweisung ohne Argumente verwendet).
Die Syntax für den Aufruf der neuen Funktion ist die gleiche, wie die Syntax für eingebaute Funktionen:
print "First Line."
newLine()
print "Second Line."
Die Ausgabe dieses Programms ist:
First line.
Second line.
Beachte die Leerzeile zwischen den beiden Zeilen mit Text. Was können wir nun machen, wenn wir mehr Zwischenraum zwischen diesen Zeilen haben möchten? Wir können die selbe Funktion mehrmals aufrufen:
print "First Line."
newLine()
newLine()
newLine()
print "Second Line."
Oder wir können eine neue Funktion, sagen wir mit Namen threeLines schreiben, die drei leere Zeilen ausgibt:
def threeLines():
newLine()
newLine()
newLine()
print "First Line."
threeLines()
print "Second Line."
Diese Funktion enthält drei Anweisungen, die alle um zwei Leerzeichen eingerückt sind. Da die nächste Anweisung nicht mehr eingerückt ist, weiß Python dass sie nicht mehr Teil der Funktionsdefinition ist.
Man sollte sich ein paar Dinge von diesem Programm einprägen:
Anmerkung: Wir werden ab hier Funktionsnamen immer mit einem paar leerer, runder Klammern nach dem Namen schreiben: type() bzw. newLine() usw. So erkennt man bereits am Namen, dass er ein Bezeichner für eine Funktion ist.
Bis jetzt mag es noch nicht klar sein, wozu die Mühe mit der Definition all dieser neuen Funktionen gut sein soll. In der Tat gibt es dafür eine Menge Gründe, aber dieses Beispiel demonstriert zwei:
Als Übung schreibe eine Funktion nineLines(), die die Funktion threeLines() benützt um neun Leerzeilen auszugeben. Wie würdest du 27 Leerzeilen ausgeben?
Setzt man die Programm-Fragmente aus Abschnitt 3.6 zusammen, dann sieht das ganze Programm so aus:
def newLine():
print
def threeLines():
newLine()
newLine()
newLine()
print "First Line."
threeLines()
print "Second Line."
Dieses Programm enthält zwei Definitionen: newLine() und threeLines(). Funktionsdefinitionen werden ausgeführt gerade so wie andere Anweisungen auch. Aber die Wirkung ist, dass eine neue Funktion erzeugt wird. Die Anweisungen im Funktionskörper werden nicht ausgeführt, bis die Funktion aufgerufen wird und die Funktionsdefinition erzeugt keine Ausgabe.
Wie nicht anders zu erwarten, muss man eine Funktion erzeugen, bevor man sie ausführen kann. In anderen Worten: die Funktionsdefinition muß ausgeführt werden, bevor die Funktion zum ersten Mal aufgerufen wird.
Als weitere Übung beginne nun mit einer lauffähigen Version dieses Programms und verschiebe die Definition von newLine() hinter die Definition von threeLines(). Was geschieht, wenn du das Programm nun ausführst?
Um sicherzustellen, dass eine Funktion definiert wird, bevor sie zum ersten Mal verwendet wird, musst du die Reihenfolge kennen, in der die Anweisungen ausgeführt werden. Diese Reihenfolge nennt man den Programmablauf
Die Programmausführung beginnt immer mit der ersten Anweisung des Programms. Die Anweisungen werden eine nach der anderen ausgeführt, in der Reihenfolge von oben nach unten.
Funktionsdefinitionen ändern nicht den Programmablauf. Wir erinnern uns daran, dass die Anweisungen im Funktionskörper nicht ausgeführt werden, bevor die Funktion aufgerufen wird. Obwohl es nicht üblich ist, kann man eine Funktion im Inneren einer anderen definieren. In diesem Fall wird die innere Definition erst ausgeführt, wenn die äußere Funktion aufgerufen wird.
Funktionsaufrufe kann man mit einem Umweg im Programmablauf vergleichen. Anstatt zur nächsten Anweisung weiterzugehen, setzt der Programmablauf mit der ersten Zeile der aufgerufenen Funktion fort, führt alle Anweisungen dort aus und kommt dann zurück um dort fortzusetzen, wo er vorher abgezweigt ist.
Das klingt ja noch einfach, bis man daran denkt, dass eine Funktion eine weitere aufrufen kann. Wenn er in der Mitte einer Funktion ist, muß der Programmablauf vielleicht zu Anweisungen einer weiteren Funktion springen, alle Anweisungen dort ausführen und möglicherweise von dort wieder zu den Anweisungen einer weiteren Funktion verzweigen ...!
Zum Glück ist Python so gemacht, dass es Buch führt darüber, wo die Programmausführung gerade ist und woher sie kam. Daher kann das Programm immer dann, wenn eine Funktion fertig ausgeführt worden ist, die Ausführung dort fortsetzen, wo es wegen des Funktionsaufrufs verzweigt hat: an der Stelle nach dem Funktionsaufruf. Wenn die Programmausführung am Ende des Programms angekommen ist, endet die Programmausführung.
Was ist die Moral von dieser verwirrenden Geschichte? Wenn du ein Programm liest, lies es nicht von oben nach unten. Folge vielmehr dem Programmablauf.
Einige der eingebauten Funktionen die du bisher benützt hast, verlangen Argumente, das sind Werte, die dafür entscheidend sind, wie die Funktion ihre Arbeit tut. Wenn du z. B. den Sinuswert einer Zahl wissen willst, musst du der sinus-Funktion mitteilen, was diese Zahl ist. Daher braucht sin() einen numerischen Wert als Argument.
Einige Funktionen verlangen die Übergabe von mehr als einem Argument. Zum Beispiel braucht pow() zwei Argumente, die Basis und den Exponenten. Im inneren der Funktion werden die übergebenen Argumente gewissen Variablen zugewiesen, die Parameter genannt werden.
Hier ist ein Beispiel einer benutzerdefinierten Funktion, die einen Parameter hat:
def printTwice(bruce):
print bruce, bruce
Diese Funktion übernimmt ein einziges Argument und weist es dem Parameter mit dem Namen bruce zu. Der Wert des Parameters (an dieser Stelle haben wir keine Idee, welcher das sein wird) wird zweimal auf dem Bildschirm ausgegeben, gefolgt von einem Zeilenende. Der Name bruce wurde gewählt, um anzudeuten, dass der Name, den du einem Parameter gibst, ganz beliebig von dir ausgewählt werden kann. Im allgemeinen wird man eine etwas illustrativeren Namen als bruce wählen.
Die Funktion printTwice() arbeitet für jeden Typ, der ausgegeben werden kann.
>>> printTwice('Spam')
Spam Spam
>>> printTwice(5)
5 5
>>> printTwice(3.14159)
3.14159 3.14159
Im ersten Funktionsaufruf ist das Argument ein String. Im zweiten ist es eine Ganzzahl. Im dritten ist es vom Typ float.
Dieselben Reglen für Zusammensetzung, die für eingebaute Funktionen gelten, gelten auch für benutzerdefinierte Funktionen. Daher können wir jede Art von Ausdruck als Argument für printTwice() verwenden:
>>> printTwice('Spam'*4)
SpamSpamSpamSpam SpamSpamSpamSpam
>>> printTwice(math.cos(math.pi))
-1.0 -1.0
Wie üblich wird der Ausdruck ausgewertet, bevor die Funktion ausgeführt wird, daher gibt printTwice hier SpamSpamSpamSpam SpamSpamSpamSpam und nicht 'Spam'*4 'Spam'*4.
Als Übung schreibe einen Funktionsaufruf von printTwice, der tatsächlich 'Spam'*4 'Spam'*4 auf den Bidschirm schreibt. Hinweis: Strings können entweder in einfache Anführungszeichen oder in doppelte Anführungszeichen gesetzt werden. Der Typ von Anführungszeichen, der nicht den String einschließt, kann im String selbst als Teil des Strings verwendet werden.
Wir können auch eine Variable als Argument verwenden:
>>> latoya = 'Eric, the half a bee.'
>>> printTwice(latoya)
Eric, the half a bee. Eric, the half a bee.
Hier ist etwas sehr Wichtiges zu beachten. Der Name der Variablen, die wir als Argument übergeben (latoya) hat nichts mit dem Namen des Parameters zu tun (bruce). Es spielt keine Rolle, wie der Wert in der aufrufenden Funktion genannt wurde; hier in printTwice(), nennen wir ihn auf jeden Fall bruce.
Wenn man eine lokale Variable im Inneren einer Funktion erzeugt, dann existiert sie nur innerhalb dieser Funktion und man kann sie außerhalb nicht verwenden.
def catTwice(part1, part2):
cat = part1 + part2
printTwice(cat)
Diese Funktion verlangt nach zwei Argumenten, verkettet sie und gibt das Ergebnis zweimal am Bildschirm aus. Wir können die Funktion mit zwei Strings aufrufen:
>>> chant1 = "Pie Jesu domine, "
>>> chant2 = "Dona eis requiem."
>>> catTwice(chant1, chant2)
Pie Jesu domine, Dona eis requiem. Pie Jesu domine, Dona eis requiem.
Wenn die Ausführung von catTwice() endet, wir die Variable cat zerstört. Wenn wir versuchen sie auszugeben, erhalten wir eine Fehlermeldung.
>>> print cat
NameError: cat
Parameter sind auch lokal. Zum Beispiel gibt es außerhalb der Funktion printTwice keine Variable bruce. Wenn du versuchst sie zu verwenden, wird sich Python mit einer Fehlermeldung beklagen.
Um besser verfolgen zu können, welche Variable wo verwendet werden kann, ist es manchmal nützlich ein sogenanntes Stackdiagramm zu zeichnen. Wie Zustandsdiagramme zeigen Stackdiagramme den Wert jeder Variablen, sie zeigen aber auch die Funktion, zu der jede Variable gehört.
Jede Funktion wird durch einen Rahmen dargestellt. Ein Rahmen ist eine Kästchen mit dem Namen einer Funktion daneben und den Parametern und Variablen der Funktion im Inneren. Das Stack-Diagramm für das vorhergehende Beispiel sieht so aus:
Die Anordnung des Stacks zeigt den Programmablauf. printTwice() wurde von catTwice() aufgerufen, und catTwice() wurde von __main__() aufgerufen; das ist ein spezieller Name für die oberste Funktion. Wenn man eine Variable außerhalb jeder Funktion erzeugt, gehört sie zu __main__().
Jeder Parameter bezieht sich auf denselben Wert wie sein entsprechendes Argument. So hat part1 denselben Wert wie chant1, part2 hat denselben Wert wie chant2 und bruce hat denselben Wert wie cat.
Wenn ein Fehler während eines Funktions-Aufrufs auftritt, gibt Python den Namen der Funktion aus und den Namen der Funktion, die sie aufgerufen hat und den Namen der Funktion, die diese aufgerufen hat ..., den ganzen Weg zurück bis __main__().
Wenn wir z. B. versuchen auf cat von innerhalb printTwice() zuzugreifen, erhalten wie einen NameError:
Traceback (innermost last):
File "test.py", line 13, in __main__
catTwice(chant1, chant2)
File "test.py", line 5, in catTwice
printTwice(cat)
File "test.py", line 9, in printTwice
print cat
NameError: cat
Diese Liste der Funktionen nennt man Ablaufverfolgung (engl.: traceback). Diese teilt dir mit, in welcher Programmdatei der Fehler auftrat, in welcher Zeile das war und welche Funktionen zu diesem Zeitpunkt in Ausführung begriffen waren. Sie zeigt auch die Programmzeile an, die den Fehler verursacht hat.
Beachte die Ähnlichkeit zwischen dieser Ablaufverfolgung und dem Stackdiagramm. Diese ist kein Zufall!
Vielleicht ist dir schon aufgefallen, dass einige Funktionen, die wir verwenden, wie etwa die mathematischen Funktionen, Ergebnisse liefern. Andere Funktionen, wie etwa newLine() führen zwar Aktionen aus, geben aber keinen Wert zurück. Daraus ergeben sich einige Fragen:
Die Antwort auf die dritte Frage ist ja, und wir werden das in Kapitel 5 tun.
Als eine Übung beantworte die anderen beiden Fragen, indem du sie mit dem Python-Interpreter ausprobierst. Wenn du eine Frage hast in Bezug darauf, was in Python erlaubt ist und was nicht, dann ist immer ein guter Weg das herauszufinden, den Interpreter zu fragen.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|