Home / LinuxUser / 2007 / 01 / Einfache Architektur

Newsletter abonnieren

Lies uns auf...

Folge LinuxCommunity auf Twitter

Top-Beiträge

War doch klar...
(243 Punkte bei 15 Stimmen)
Re: War doch klar...
(179 Punkte bei 5 Stimmen)
Re: Skype für 64-Bit-Prozessor u. Suse 12.1
(161 Punkte bei 4 Stimmen)
Kubuntu verliert Finanzierung
(130 Punkte bei 4 Stimmen)
Offen fürs Geschäft
(80 Punkte bei 4 Stimmen)

Heftarchiv

LinuxUser Heftarchiv

EasyLinux Heftarchiv

Ubuntu User Heftarchiv

Ubuntu User Heftarchiv

Partner-Links:

Shopping
Topsuche
 
Yatego Deutschlands größte Shoppingmall. 10000 Shops,
3.5 Mio Artikel. Alle Bestseller, Servertechnik und Technik Themenwelten.

Notebooks und Netzwerkhardware bei Mercateo günstig kaufen.
Internet Telefonie mit VoIP Telefonen von Gigaset
Das B2B Portal www.Linx.de informiert über Produkte und Dienstleistungen.
Günstige Digitalkameras finden Sie im Preisvergleich.

Einfache Architektur

Objektorientiertes Programmieren mit Python

01.01.2007 Spezialmethoden und geschicktes Vererben von Klassenmethoden vereinfachen das Programmieren mit Python drastisch. Unser Workshop zeigt, welche Tricks die Skriptsprache auf Lager hat.

Im ersten Teil der Python-Workshops (LinuxUser 09/2006) ging es um den interaktiven Interpreter, Datentypen und Ablaufstrukturen. Der zweite Teil (LinuxUser 10/2006) behandelte das Aufteilen von Code in Funktionen, Module und Pakete. Der dritte Teil (LinuxUser 11/2006) befasste sich mit Fehlern und Ausnahmen und bot einen Einstieg in das objektorientierte Programmieren, das dieser Artikel vertieft.

Im dritten Teil des Workshops hat sich durch das Bearbeiten des Textes eine sinnentstellende Aussage in Bezug auf überladene Methoden ergeben: Überladene Methoden (gleicher Name, aber unterschiedliche Parameterlisten) wie in C++ oder Java gibt es in Python nicht. Man erreicht eine ähnliche Funktionalität, indem man den auszuführenden Code nach den im Aufruf verwendeten Parameternamen auswählt. Alternativ kann man prüfen, ob ein übergebener Parameter eine bestimmte Schnittstelle unterstützt. Die direkte Nachbildung der Typunterscheidung mit der Funktion isinstance ist nicht zu empfehlen, da sie das Duck Typing aushebelt.

Arg abstrakt

Wie in einigen anderen Programmiersprachen gibt es auch in Python das Prinzip einer abstrakten Klasse. Von dieser sollte es keine Instanzen geben. Die abstrakte Basisklasse enthält nur allgemeine (abstrakte) Definitionen; erst die abgeleiteten Klassen definieren sinnvolles, verwendbares Verhalten. Listing 1 zeigt ein Beispiel.

Listing 1
import math
class Form(object):
    def flaeche(self):
        raise NotImplementedError("in abgeleiteter Klasse definieren")
class Quadrat(Form):
    def __init__(self, seitenlaenge=0.0):
        self.seitenlaenge = seitenlaenge
    def flaeche(self):
        return self.seitenlaenge * self.seitenlaenge
class Kreis(Form):
    def __init__(self, radius=0.0):
        self.radius = radius
    def flaeche(self):
        return math.pi * self.radius * self.radius
# ggf. weitere Form-Klassen
# …
def gesamtflaeche(formen):
    # "generator comprehension"
    flaechen = (form.flaeche() for form in formen)
    return sum(flaechen, 0.0)
if __name__ == '__main__':
    q = Quadrat(10)
    k = Kreis(5)
    # Ausgabe 178.53981634
    print gesamtflaeche([q, k])

Die abstrakte Klasse Form (Zeilen 3 bis 5) definiert die Methode flaeche nur als Platzhalter. Obwohl es in Python nicht zwingend notwendig ist, eine abstrakte Methode wie Form.flaeche zu notieren, dient es manchmal als Dokumentation; die Methode löst lediglich eine Ausnahme vom Typ NotImplementedError aus (Zeile 5). In den abgeleiteten Klassen Quadrat und Kreis (Zeilen 7 bis 19) sind die konkreten Arten der Flächenberechnung bekannt, und flaeche ist jeweils implementierbar.

Da alle Formen eine Methode flaeche haben, berechnet die Funktion gesamtflaeche (Zeilen 24 bis 27) auf einfache Weise die Summe der Flächen, die die Sequenz formen enthält.

Duck Typing

Beim Bearbeiten der Objekte in der Sequenz formen greift ein Prinzip namens Duck Typing. Der Ursprung dieses seltsamen Namens ist der Satz "If it looks like a duck and quacks like a duck, it must be a duck." (deutsch: Wenn es wie eine Ente aussieht und wie eine Ente quakt, muss es eine Ente sein.) Es gibt keine Typdefinitionen für die Parameter wie in C++ oder Java – also keine Vorschrift, die vorgibt, dass die Klassen der Objekte in formen von der gleichen Basisklasse abstammen müssen. Deshalb dürfen Sie Quadrat und Kreis direkt von object ableiten; die abstrakte Klasse Form ist nicht nötig.

Duck Typing ist in Python sehr gebräuchlich. Ein anderes Beispiel sind dateiartige Objekte, die bestimmte Methoden von Dateiobjekten besitzen. So dürfen Sie beispielsweise an eine Funktion, die die Methoden read, readline und readlines eines Dateiobjekts erwartet, auch ein Objekt vom Typ StringIO[1] übergeben. Das ist sinnvoll, wenn die Daten, die Sie an die Funktion durchreichen, nicht aus einem Dateisystem stammen. Listing 2 zeigt ein entsprechendes Beispiel.

Listing 2
#! /usr/bin/env python
def nummerierte_datei(dateiobjekt):
    """
    Eine Generatorfunktion, die die Zeilen aus der Datei
    file_object mit dreistelligen Zeilennummern versieht.
    """
    zeilennummer = 1
    while True:
        try:
            zeile = dateiobjekt.next()
        except StopIteration:
            raise
        yield "%03d %s" % (zeilennummer, zeile)
        zeilennummer += 1
def ausgabe_nummerierte_datei(dateiobjekt):
    for zeile in nummerierte_datei(dateiobjekt):
        print zeile,
if __name__ == '__main__':
    programmdatei = open("nummerierte_datei.py")
    ausgabe_nummerierte_datei(programmdatei)
    programmdatei.close()
    print
    # dies sei im Programm generierter Text
    text = """\
Dies ist ein mehrzeiliger
Text, der in die Funktion
ausgabe_nummerierte_datei
gesteckt wird."""
    import StringIO
    textdatei = StringIO.StringIO(text)
    ausgabe_nummerierte_datei(textdatei)
    textdatei.close()

Bei nummerierte_datei (Zeilen 3 bis 15) handelt es sich um eine Generatorfunktion, wie im zweiten Teil des Python-Kurses beschrieben. Die voranzustellenden Zeilennummern sollen mit 001 beginnen, deshalb bekommt die Variable zeilennummer in Zeile 8 den Startwert 1. Nun durchläuft das Programm bis auf Widerruf (siehe While-Bedingung) die durch dateiobjekt beschriebene Datei. Jede gelesene Zeile erzeugt in Programmzeile 11 eine neue Zeichenkette, die in Codezeile 14 eine Zeilennummer vorangestellt bekommt.

Finden sich keine weiteren Zeilen in der Datei, löst die Codezeile 11 die Ausnahme StopIteration aus, die die Zeile 13 einfach nach oben durchreicht. Dadurch bricht die For-Schleife korrekt ab, die in ausgabe_nummerierte_datei (Programmzeilen 17 bis 19) über das Ergebnis von nummerierte_datei iteriert.

Natürlich wäre es denkbar, mit einer Funktion zu arbeiten, die die zu nummerierenden Zeilen als Sequenz annimmt. Das setzt allerdings voraus, dass das Skript die ganze Datei auf einmal einliest, was bei großen Dateien problematisch wäre.

Der Testcode in den Zeilen 22 bis 37 verwendet zwei dateiartige Objekte, um die Zeilen daraus zu lesen und sie nummeriert auszugeben. In den Programmzeilen 23 bis 25 ist es eine gewöhnliche Datei, nämlich das Programm selbst. Die Zeilen 28 bis 37 erzeugen dagegen ein dateiartiges Objekt direkt aus einer Zeichenkette. Dieses StringIO-Objekt besitzt, wie auch ein echtes Dateiobjekt, eine Methode next, die in Zeile 11 zum Einsatz kommt. Listing 3 zeigt die Programmausgabe, wobei die Ausgabezeilen 6 bis 32 fehlen.

Listing 3
001 #! /usr/bin/env python
002
003 def nummerierte_datei(dateiobjekt):
004     """
005     Eine Generatorfunktion, die die Zeilen aus der Datei
.
.
.
033 wird."""
034     import StringIO
035     textdatei = StringIO.StringIO(text)
036     ausgabe_nummerierte_datei(textdatei)
037     textdatei.close()
001 Dies ist ein mehrzeiliger
002 Text, der in die Funktion
003 ausgabe_nummerierte_datei
004 gesteckt wird."""

Dieser Code setzt nicht voraus, dass sich Pythons Dateiklasse und StringIO von der gleichen Basisklasse ableiten. Es reicht, eine bestimmte Schnittstelle zu definieren (hier die Methode next) und eine Ausnahme StopIteration am Ende der Datei zu erzeugen.

Einem Freund empfehlen    Druckansicht Bookmark and Share
Kommentare

1451 Hits
Wertung: 165 Punkte (8 Stimmen)

Schlecht Gut

Infos zur Publikation

Infos zur Publikation

LinuxUser 03/2012

Aktuelle Ausgabe kaufen:

Heft bestellen Heft als PDF kaufen

LinuxUser erscheint monatlich und kostet in der Nomedia-Ausgabe EUR 5,50 und mit DVD EUR 8,50. Weitere Informationen zum Heft finden Sie auf der LinuxUser-Homepage.

Im LinuxUser-Probeabo erhalten Sie drei Ausgaben für 3 Euro. Das Jahresabo (ab EUR 56,10) können Sie im LNM-Shop bestellen.

Tipp der Woche

Duden Korrektor unter 64-Bit
Duden Korrektor unter 64-Bit
Tim Schürmann, 06.02.2012 10:36, 0 Kommentare

Der Duden Korrektor bietet eine äußerst nützliche Rechtschreib- und Grammatikkorrektur für LibreOffice und bringt in der aktuellen Version 8 e...

Aktuelle Fragen

Ubuntu 11.10 Compiz 3D Cube flackern
Moritz Obenauer, 12.02.2012 12:25, 0 Antworten
Hallo! Ich habe Ubuntu 11.10 mit Unity und den Compiz 3D Cube ein gerichtet. Er funktioniert...
Suse 12.1 Bootvorgang bleibt seit Update stehen
Wimpy *, 12.02.2012 09:22, 1 Antworten
Seit Update auf Kernel 3.1.9-1.4-desktop i686 bleibt der Bootvorgang stehen. Es erscheint der gr...
N24 Stick (Huawei E173) und Ubuntu 11.04
Patrick Obenauer, 11.02.2012 11:54, 1 Antworten
Hallo zusammen! Ich benutze einen alten Laptop, der mit Ubuntu 11.04 flott und problemlos läuft....
Wie kann man beim Einsatz von Compiz die Fenster-Dekoration einstellen?
GoaSkin , 10.02.2012 20:12, 0 Antworten
Hallo, ich nutze Linux Mint mit dem Gnome-Derivat Mate. Da die Distribution Compiz nicht autom...
rndc reload zone - failed bad zone
Ludwig jun. B., 06.02.2012 16:08, 2 Antworten
Schönen guten Tag, ich habe folgendes Problem. Immer wenn ich folgendes Kommando ausführen bek...