5. Funktionen I

Eine Funktion ist etwas, das einen oder mehrere Wert(e) nimmt (die Argumente) und einen oder mehrere Wert(e) zurückgibt (das Ergebnis). Das funktioniert fast wie in der Mathematik:

2
f :x ↦→  x      f(2) = 4     f(7) = 49.

Wir kennen schon type. Einige weitere in Python eingebaute Funktionen:

  • raw_input: Druckt ggf. ihr Argument und gibt eine Eingabe des Benutzers als String zurück
  • int: Gibt ihr Argument als ganze Zahl zurück
  • float: Gibt ihr Argument als Fließkommazahl zurück
  • str: Gibt ihr Argument als String zurück
  • len: Gibt die “Länge” ihres Arguments zurück
>>> eurInMark = 1.95583
>>> s = raw_input("EUR? ")
EUR? 10
>>> float(s)*eurInMark
19.558299999999999
>>> int(float(s)*eurInMark*100)/100
19
>>> int(float(s)*eurInMark*100)/100.
19.550000000000001

Funktionen wie float oder len kann man sich vorläufig wie Kommunikationsmittel mit Werten vorstellen: float("19") fragt den Wert "19", was er als Fließkommazahl sei, und len("19") fragt ihn, für wie lang er sich hält.

Es kann passieren, dass ein Wert keine Antwort auf so eine Frage hat. Python reagiert dann mit einem Fehler:

>>> int("23.3")
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ValueError: invalid literal for int(): 23.3
>>> float("zwei komma drei")
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ValueError: invalid literal for float(): zwei komma drei
>>> len(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: len() of unsized object

Die Funktion len funktioniert also nach dieser Aussage nur auf Werte, die “sized” sind. Wir werden etliche Werte dieser Sorte kennenlernen; im Augenblick haben nur Strings so eine Größe, nämlich gerade ihre Länge in Zeichen.

Im Übrigen habe ich hier gelogen. Wenigstens in modernen Python-Versionen sind str, float und int keine normalen Funktionen mehr. Das gibt Python auch freimütig zu:

>>> type(len)
<type 'builtin_function_or_method'>
>>> type(str)
<type 'type'>

Was es mit diesem type ’type’ auf sich hat, werden wir in einer Weile sehen.

Exkurs: Integer Division und Rundungsfehler

Am Beispiel oben sieht man ein paar kritische Punkte im Umgang mit Zahlen im Computer.

(1) 1955/100 ist für Python 19. Grund: “/” ist (bisher) ganzzahlige Division, wenn beide Operanden ganzzahlig sind. Bei der ganzzahligen Division (integer division) werden alle Nachkommastellen, die bei einer Division auftauchen, weggeworfen (oder die Reste ignoriert). Demnach ist 1/2==0.Abhilfe durch Kennzeichnung eines Operanden als float:

>>> 1/float(2)
0.5
>>> 1/2.
0.5

Das verwirrende Verhalten des / gilt als “Warze” (und Erbe von C) und wird in künftigen Python-Versionen anders sein. In Python 2.2 kann man ein vernünftigeres Verhalten einschalten (die integer division wird dann durch // markiert):

>>> from __future__ import division
>>> 1/2
0.5

(2) 1955/100. ist nicht genau 19.55. Grund: Fließkommazahlen werden nur mit endlicher Genauigkeit und binär (also mit den Ziffern 0 und 1 statt mit den Ziffern von 0 bis 9) dargestellt. 19.55 bricht dezimal ab, binär aber nicht. Die Folge sind Rundungsfehler, mit denen Fließkommazahlen immer gerechnet werden muss.

Folge: Vergleiche von Fließkommazahlen sind unzuverlässig und sollten umgangen oder “fuzzy” gemacht werden.

Die Hässlichkeit fällt hier auf, weil die Zahl als Ergebnis einer Anweisung dargestellt wurde, so dass Python die volle Genauigkeit (bzw. Ungenauigkeit) darstellt. Eine normale Ausgabe sieht nicht so schlimm aus:

>>> print int(float(s)*eurInMark*100)/100.
19.55

(was natürlich am Problem nichts ändert).

Übungen zu diesem Abschnitt

Ihr solltet euch wenigstens an den rötlich unterlegten Aufgaben versuchen

(1)

Probiert alle Beispiele auf dieser Seite aus. Macht euch klar, warum welche Beispiele funktionieren und warum die, die nicht funktionieren, einen Fehler werfen. Wenn ihr dafür sorgen müsstet, dass int("23.3") funktionieren sollte, statt einen Fehler zu werfen, was würdet ihr tun?

(2)

Führt

>>> print type(raw_input())

für verschiedene Eingaben aus, etwa 3.3, hallo, //(), 027, 3+3 oder "ein String". Was beobachtet ihr? Warum könnten die Python-MacherInnen das so gemacht haben?

(3)

Vergleicht die Ausgaben von

>>> 1/2.,1/4.,1/8.,1/16.,1/32.

und

>>> 1/5.,1/10.,1/20.,1/25.,1/40.

Was fällt auf? Könnt ihr euch (insbesondere im Hinblick darauf, dass Computer intern nur Nullen und Einsen speichern können) einen Reim auf dieses Verhalten machen?


Markus Demleitner

Copyright Notice