====== Objektorientierte Programmierung in Python ======
Beispiel: Schildkroete
import math
class Schildkroete:
# Konstruktor
def __init__(self, zeichenbrett):
self.__x = 100
self.__y = 100
self.__winkel = 0
self.__bogenmass = 0
# Methoden (self muss immer der erste Parameter sein)
def setX (self, x):
self.__x=x
def getX (self):
return self.__x
def setY (self, y):
self.__y=y
def getY (self):
return self.__y
def setWinkel (self,w):
self.__winkel=w
self.__bogenmass = math.pi * 2 * self.__winkel / 360
def getWinkel (self):
return this.__winkel
def gehe(self, n):
dx = n * math.sin (self.__bogenmass)
dy = n * math.cos (self.__bogenmass)
xNeu = self.__x + dx
yNeu = self.__y + dy
zeichenbrett.create_line(self.__x, self.__y, xNeu, yNeu)
self.__x = xNeu
self.__y = yNeu
def dreheLinks(self, w):
self.__winkel += w + 360
self.__winkel = self.__winkel % 360
self.__bogenmass = math.pi * self.__winkel / 360 * 2
def dreheRechts(self, w):
self.__winkel -= w + 360
self.__winkel = self.__winkel % 360
self.__bogenmass = math.pi * self.__winkel / 360 * 2
# Import der Tkinter-Bibliotek, mit deren Hilfe gezeichnet wird
from tkinter import *
# Fensteranwendung mit Namen fester erstellen
fenster = Tk()
# Groesse des Fensters
breite = 600
hoehe = 600
rand = 5
# erstellt die Komponente , auf der man zeichnen kann
zeichenbrett = Canvas (fenster, width=breite, height=hoehe)
# zeichnet das Haus des Nikolaus
# platziert die erzeugten Elemente (Canvas, Button, Textfelder, ...) im Fenster
zeichenbrett.pack()
# Endlosschleife:
fenster.mainloop()
Die objektorientierte Programmierung in Python wird an einem Beispiel demonstriert, das dem Buch "Python" aus dem Verlag Galileo Computing entnommen ist. Ziel ist es, ein sehr einfaches Programm für eine Bank zu entwickeln, mit dessen Hilfe Konten verwaltet sowie Überweisungen und Ein- bzw. Auszahlungen vorgenommen werden können. Dies geschieht mit Objekten der selbst definierten Klasse "''Konto''".
===== Klassen =====
Zur Definition einer Klasse in Python dient das Schlüsselwort ''class'', dem der Name der Klasse folgt. Der Zusatz ''(object)'' wird im Abschnitt "Vererbung" erklärt.
class Konto(object):
...
...
Ein neues Objekt (also eine Instanz) der Klasse wird durch Angabe des Klassennamens mit einem Klammerpaar erreicht:
k1 = Konto()
===== Methoden =====
Die Definition einer Methode unterscheidet sich nur geringfügig von der Definition einer Funktion: Sie steht innerhalb des von ''class'' eingeleiteten Blocks und enthält als ersten Parameter eine Referenz auf die Instanz, über die sie aufgerufen wird. In diesem Beispiel ist das das Objekt selbst, deshalb heißt diese Referenz ''self''.
class Konto(object):
def ueberweisung(self, ziel, betrag):
...
def einzahlen(self, betrag):
...
def auszahlen(self, betrag):
...
===== Attribute =====
Attribute sind die Eigenschaften eines Objektes. Im Beispiel der Klasse "Konto" sind die Attribute: Kontoinhaber, Kontonummer, Kontostand, maximaler Tagesumsatz, heutiger Tagesumsatz.
===== Konstruktor =====
Der Konstruktor ist die Methode, die genau einmal bei der Erzeugung des Objektes aufgerufen wird. Da es die Aufgabe des Konstruktors ist, das Objekt in einen definierten Ausgangzustand zu verstetzen, sollten hier auch die Attribute definiert werden. Konstruktoren haben in Python immer den Namen ''__init__'' (zwei Unterstriche vorher und nachher).
class Konto(object):
def __init__(self, inhaber, kontonummer, kontostand, max_tagesumsatz=1500):
self.Inhaber = inhaber
self.Kontonummer = kontonummer
self.Kontostand = kontostand
self.MaxTagesumsatz = max_tagesumsatz
self.UmsatzHeute = 0
# hier kommen die restlichen Methoden hin
===== Destruktor =====
Der Destruktor ist die Methode zum Vernichten eines Objektes (in unserem Beispiel: Kontoauflösung). Er hört auf den Namen ''__del__''.
class Konto(object):
# hier kommt der Konstruktor hin
def __del__(self):
print "Das Konto wurde aufgelöst"
# hier kommen die restlichen Methoden hin
===== Kapselung =====
Attribute und Methoden, die von außen nicht sichtbar sein sollen, können so gekennzeichnet werden, dass nur die Klasse selbst darauf zugreifen kann. Die Manipulation der Objekte erfolgt dann ausschließlich über die von außen sichtbaren Methoden und Attribute.\\
In Python wird über den Namen eines Attributes oder einer Methode festgelegt, ob es von außen sichtbar (public) oder unsichtbar (private) ist. Private Attribute und Methoden beginnen mit zwei Unterstrichen.
Das Beispiel wird so geändert, dass die Attribute nicht direkt geändert werden können:
class Konto(object):
def __init__(self, inhaber, kontonummer, kontostand, max_tagesumsatz=1500):
self.__Inhaber = inhaber
self.__Kontonummer = kontonummer
self.__Kontostand = kontostand
self.__MaxTagesumsatz = max_tagesumsatz
self.__UmsatzHeute = 0
# hier kommen die restlichen Methoden hin
===== Getter und Setter =====
Sind die Attribute eines Objektes nicht mehr sichtbar, benötigt man von außen zugängliche Methoden, um Attribute auszulesen (Get-Methode oder Getter) oder zu schreiben (Set-Methode oder Setter).\\ In unserem Beispiel wird ein Getter für den Kontostand und das Tageslimit eingefügt. Außerdem schreiben wir einen Setter für das Tageslimit, der gleichzeitig überprüft, ob der übergebene Wert zulässig ist:
class Konto(object):
# hier kommt der Konstruktor hin
# Getter für den Kontostand
def kontostand(self):
return self.__Kontostand
# Getter für das Umsatzlimit
def maxTagesumsatz(self):
return self.__MaxTagesumsatz
# Setter für das Umsatzlimit
def setMaxTagesumsatz(self, neues_limit):
if neues_limit > 0:
self.__MaxTagesumsatz = neues_limit
return True
else:
return False
# hier kommen die restlichen Methoden hin
===== Versteckte Getter und Setter =====
In Python ist es möglich, die Get- und Set-Methoden zu "verstecken". Nach außen sieht es so aus, als könnten die Attribute direkt gelesen oder geschrieben werden. Tatsächlich wird aber z.B. bei der Wertzuweisung eine Set-Methode aufgerufen, in der die Werte auf Zulässigkeit geprüft werden.\\
Im folgenden Beispiel wird ''MaxTagesumsatz'' durch den Datentyp ''property'' zum sogenannten //Managed Attribute//, der erste Parameter von ''property'' gibt eine Referenz auf die Getter-Methode, der zweite Parameter auf die Setter-Methode.
class Konto(object):
# hier kommt der Konstruktor hin
# Getter für den Kontostand
def kontostand(self):
return self.__Kontostand
# Getter für das Umsatzlimit
def maxTagesumsatz(self):
return self.__MaxTagesumsatz
# Setter für das Umsatzlimit
def setMaxTagesumsatz(self, neues_limit):
if neues_limit > 0:
self.__MaxTagesumsatz = neues_limit
return True
else:
return False
MaxTagesumsatz = property(maxTagesumsatz, setMaxTagesumsatz)
# hier kommen die restlichen Methoden hin
===== Statische Member =====
Statische Member, also statische Attribute oder statische Methoden, werden verwendet, um Daten und Funktionen zu erstellen, auf die auch ohne das Erzeugen einer Instanz der Klasse zugegriffen werden kann. Ein statisches Attribut existiert in der Klasse genau einmal, alle Objekte der Klasse teilen sich das Attribut.\\
In Python werden statische Attribute außerhalb des Konstruktors direkt im ''class''-Block definiert. Im folgenden Beispiel wird das statische Attribut ''Anzahl'' definiert, das die Zahl der erzeugten Konten speichert. Dazu wird es mit dem Wert 0 initialisiert und bei jedem Aufruf des Konstruktors um 1 erhöht. Beim Aufruf des Destruktors wird der Wert um 1 reduziert. Statt ''self'' muss eine Referenz auf die Klasse, hier also ''Konto'' vorangestellt werden.
class Konto(object):
Anzahl = 0
def __init__(self, inhaber, kontonummer, kontostand, max_tagesumsatz=1500):
self.__Inhaber = inhaber
self.__Kontonummer = kontonummer
self.__Kontostand = kontostand
self.__MaxTagesumsatz = max_tagesumsatz
self.__UmsatzHeute = 0
Konto.Anzahl += 1
def __del__(self):
Konto.Anzahl -= 1
# hier kommen die restlichen Methoden hin
===== Überladen =====
Das Überladen von Methoden ist in Python nicht vorgesehen.
===== Vererbung =====
Eine Subklasse erbt die Attribute und Methoden einer Basisklasse, indem die Basisklasse bei der Definition in Klammern angegeben wird. Bei dem Beispiel der Klasse Konto werden durch die Angabe ''class Konto(object)'' die grundlegenden Eigenschaften eines Objektes vererbt. Soll z.B. eine neue Klasse "Bausparvertrag" die Attribute und Methoden der Klasse "Konto" übernehmen, lautet die Definition
class Bausparvertrag(Konto):
...
...
===== Überschreiben =====
Wird eine Methode geerbt aber in anderer Weise benötigt, so kann sie überschrieben werden. Überschreiben heißt im Prinzip neu schreiben oder ersetzen. Die Bezeichnung überschreiben ist nur insofern gehaltvoller, als dass wichtig ist, dass der Name der Methode erhalten bleibt.\\
Ist eine Methode in der Subklasse überschrieben worden, so lässt sich die gleichnamige Methode der Basisklasse aufrufen, indem statt ''self'' der Klassenname vorangestellt wird.
===== Mehrfachvererbung =====
In Python ist es möglich, dass eine Subklasse die Attribute und Methoden von mehreren Basisklassen erbt. Existieren beispielsweise die Klassen "Haus" und "Boot", so könnte eine Klasse "Hausboot" definiert werden, die alle Attribute und Methoden eines Hauses und alle Attribute und Methoden eines Bootes erbt. (In Java gibt es die Möglichkeit der Mehrfachvererbung nicht, da die Mehrfachvererbung eine häufige Fehlerquelle darstellt.)
===== Polymorphie =====
Polymorphie bedeutet, dass eine Methode mit gleichem Namen in verschiedenen Klassen existiert. Welche Methode für ein gegebenes Objekt benutzt wird, wird erst zur Laufzeit festgelegt, denn dies ist abhängig davon, welcher Klasse das Objekt dann angehört.
===== Beispiel =====
==== Erläuterung ====
Als Beispiel dient wieder die einfache Bank-Software, die um einige Funktionen erweitert wird, um möglichst viele der oben angesprochenen Punkte umzusetzen. Das Programm selber macht noch nichts, sondern legt nur die Klassen mit ihren Attributen und Methoden fest. Die Erzeugung von Objekten und das Aufrufen der Methoden geschieht von der Kommandozeile aus. Folgende Punkte sollen implementiert werden:
* Die oben schon benutzte Klasse "Konto" soll eine Subklasse von "Zaehler" sein. Die Klasse "Zaehler" zählt die vorhandenen Instanzen, kann also angeben, wie viele Konten existieren. Dies geschieht mit einem statischen Attribut.
* Die Klasse "Konto" bekommt einen versteckten Getter und Setter für den maximalen Tagesumsatz.
* Es wird eine weitere Klasse "Angestellte" als Erbe von "Zaehler" definiert. Damit können nun nicht nur die Konten, sondern nun auch die Angestellten der Bank verwaltet und gezählt werden.
* Die zwei weiteren Klassen "Sekretaerin" und "Bankdirektor" erben die Attribute und Methoden von "Angestellte" und erweitern diese.
==== Python-Code ====
class Zaehler(object):
Anzahl = 0
def __init__(self):
type(self).Anzahl += 1
def __del__(self):
type(self).Anzahl -= 1
class Konto(Zaehler):
# Konstruktor für Konto
def __init__(self, inhaber, kontonummer, kontostand, max_tagesumsatz=1500):
Zaehler.__init__(self)
self.__Inhaber = inhaber
self.__Kontonummer = kontonummer
self.__Kontostand = kontostand
self.__MaxTagesumsatz = max_tagesumsatz
self.__UmsatzHeute = 0
print "Es wurde ein neues Konto eröffnet!"
# Destruktor für Konto
def __del__(self):
Zaehler.__del__(self)
print "Das Konto wurde aufgelöst!"
# Betrag auf Konto einzahlen
def einzahlen(self, betrag):
if self.__UmsatzHeute + betrag <= self.__MaxTagesumsatz:
self.__Kontostand += betrag
self.__UmsatzHeute += betrag
print "Der gewünschte Betrag wurde eingezahlt."
else:
print "Eine Einzahlung ist leider nicht möglich!"
# Kontodaten anzeigen
def anzeigen(self):
print "Kontoinhaber: ",self.__Inhaber
print "Kontonummer: ",self.__Kontonummer
print "Kontostand: %.2f Euro"%(self.__Kontostand)
print "Maximaler Tagesumsatz: %.2f Euro"%(self.__MaxTagesumsatz)
print "Heutiger Umsatz: %.2f Euro"%(self.__UmsatzHeute)
# Getter für das Umsatzlimit
def maxTagesumsatz(self):
return self.__MaxTagesumsatz
# Setter für das Umsatzlimit
def setMaxTagesumsatz(self, neues_limit):
if type(neues_limit) in (float, int) and neues_limit > 0:
self.__MaxTagesumsatz = neues_limit
print "Das Limit wurde geändert!"
else:
print "Unzulässiger Wert! Das Limit wurde nicht geändert."
# Managed Attribute mit verstecktem Getter und Setter
MaxTagesumsatz = property(maxTagesumsatz, setMaxTagesumsatz)
class Angestellter(Zaehler):
# Konstruktor für Angestellte
def __init__(self, name, stundenlohn, stunden_pro_woche):
Zaehler.__init__(self)
self.Name = name
self.Stundenlohn = stundenlohn
self.StundenProWoche = stunden_pro_woche
# Personaldaten anzeigen
def anzeigen(self):
print "Name: ",self.Name
print "Stundenlohn: ",self.Stundenlohn
print "Wochenstunden:",self.StundenProWoche
class Sekretaerin(Angestellter):
# Konstruktor für Sekretärin
def __init__(self, name):
Angestellter.__init__(self, name, 15, 30)
class Bankdirektor(Angestellter):
# Konstruktor für Bankdirektor
def __init__(self, name, dienstwagen):
Angestellter.__init__(self, name, 150, 50)
self.Dienstwagen = dienstwagen
# Personaldaten anzeigen (Methode überschreiben)
def anzeigen(self):
Angestellter.anzeigen(self)
print "Dienstwagen: ",self.Dienstwagen
==== Ausprobieren im interaktiven Modus ====
Konten einrichten und auflösen, zählen der vorhandenen Konten:
>>> Konto.Anzahl
0
>>> k1=Konto("Max",1234,100)
Es wurde ein neues Konto eröffnet!
>>> k2=Konto("Moritz",5678,249.90,1000)
Es wurde ein neues Konto eröffnet!
>>> Konto.Anzahl
2
>>> k1.Anzahl
2
>>> del k1
Das Konto wurde aufgelöst!
>>> k1.Anzahl
Traceback (most recent call last):
File "", line 1, in
k1.Anzahl
NameError: name 'k1' is not defined
>>> Konto.Anzahl
1
>>> del k2
Das Konto wurde aufgelöst!
>>> Konto.Anzahl
0
Anzeigen und einzahlen unter Berücksichtigung des Tagesumsatzes:
>>> k1=Konto("Bibbi Blocksberg",1080965,2745.2,1000)
Es wurde ein neues Konto eröffnet!
>>> k1.anzeigen
>
>>> k1.anzeigen()
Kontoinhaber: Bibbi Blocksberg
Kontonummer: 1080965
Kontostand: 2745.20 Euro
Maximaler Tagesumsatz: 1000.00 Euro
Heutiger Umsatz: 0.00 Euro
>>> k1.einzahlen(800)
Der gewünschte Betrag wurde eingezahlt.
>>> k1.anzeigen()
Kontoinhaber: Bibbi Blocksberg
Kontonummer: 1080965
Kontostand: 3545.20 Euro
Maximaler Tagesumsatz: 1000.00 Euro
Heutiger Umsatz: 800.00 Euro
>>> k1.einzahlen(300)
Eine Einzahlung ist leider nicht möglich!
>>> k1.anzeigen()
Kontoinhaber: Bibbi Blocksberg
Kontonummer: 1080965
Kontostand: 3545.20 Euro
Maximaler Tagesumsatz: 1000.00 Euro
Heutiger Umsatz: 800.00 Euro
Benutzung des verwalteten Attributes "MaxTagesumsatz", das die versteckte Get- und Set-Methode aufruft:
>>> k1=Konto("Donald Duck",246810,120.55,200)
Es wurde ein neues Konto eröffnet!
>>> k1.MaxTagesumsatz
200
>>> k1.MaxTagesumsatz = 400
Das Limit wurde geändert!
>>> k1.MaxTagesumsatz = -2
Unzulässiger Wert! Das Limit wurde nicht geändert.
>>> k1.MaxTagesumsatz = "Unsinn"
Unzulässiger Wert! Das Limit wurde nicht geändert.
>>> k1.anzeigen()
Kontoinhaber: Donald Duck
Kontonummer: 246810
Kontostand: 120.55 Euro
Maximaler Tagesumsatz: 400.00 Euro
Heutiger Umsatz: 0.00 Euro
Definieren einiger Objekte des Typs "Angestellter", ausführen der geerbten und überschriebenen Methode "anzeigen"
>>> a=Angestellter("Hans Wurst",20,40)
>>> s=Sekretaerin("Bibbi Bleistift")
>>> b=Bankdirektor("Siggi Superreich","Cadillac")
>>> a.anzeigen()
Name: Hans Wurst
Stundenlohn: 20
Wochenstunden: 40
>>> s.anzeigen()
Name: Bibbi Bleistift
Stundenlohn: 15
Wochenstunden: 30
>>> b.anzeigen()
Name: Siggi Superreich
Stundenlohn: 150
Wochenstunden: 50
Dienstwagen: Cadillac
Zählen der Objekte. Beachte: Eine Sekretärin wird nicht als "Angestellter" gezählt, ebenso wenig wie der Bankdirektor.
>>> k1=Konto("Max",123,100)
Es wurde ein neues Konto eröffnet!
>>> k2=Konto("Moritz",456,100)
Es wurde ein neues Konto eröffnet!
>>> a=Angestellter("Hans",20,40)
>>> b=Bankdirektor("Siggi","Fiat")
>>> s1=Sekretaerin("Bibbi")
>>> s2=Sekretaerin("Lilli")
>>> Konto.Anzahl;Angestellter.Anzahl;Sekretaerin.Anzahl;Bankdirektor.Anzahl
2
1
2
1
>>> del a
>>> Konto.Anzahl;Angestellter.Anzahl;Sekretaerin.Anzahl;Bankdirektor.Anzahl
2
0
2
1
>>> del s2
>>> Konto.Anzahl;Angestellter.Anzahl;Sekretaerin.Anzahl;Bankdirektor.Anzahl
2
0
1
1