Home / LinuxUser / 2007 / 01 / Einfache Architektur

Newsletter abonnieren

Lies uns auf...

Folge LinuxCommunity auf Twitter

Top-Beiträge

Mandriva gibt Distribution in die Hände der Community
(268 Punkte bei 24 Stimmen)
Neues vom Systemd
(179 Punkte bei 5 Stimmen)
Mandriva in Nöten
(161 Punkte bei 4 Stimmen)
Mageia 2 ist fertig
(161 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

Numerische Typen

Es existieren zahlreiche Methoden [6], die Ihnen dabei helfen, neue numerische Datentypen (wie Pythons eingebaute Typen int, complex, aber auch in Modulen definierte wie datetime.timedelta) zu erzeugen. Die Beispiel-Klasse in Listing 5 enthält diverse Methoden zum Verarbeiten von zweidimensionalen Vektoren.

Listing 5
# coding: iso-8859-1
import math
class Vektor(object):
    def __init__(self, x=0.0, y=0.0):
        self.x = float(x)
        self.y = float(y)
        self._cls = self.__class__
    def __pos__(self):
        return self._cls(self.x, self.y)
    def __neg__(self):
        return -1.0 * self
    def __add__(self, rechte_seite):
        return self._cls(self.x+rechte_seite.x, self.y+rechte_seite.y)
    def __sub__(self, rechte_seite):
        return self + (-rechte_seite)
    def __mul__(self, rechte_seite):
        try:
            rechte_seite.x
        except AttributeError:
            # rechte_seite ist ein Skalar
            skalar = rechte_seite
            return self._cls(skalar * self.x, skalar * self.y)
        else:
            # rechte_seite ist ein Vektor
            return self.x*rechte_seite.x + self.y*rechte_seite.y
    def __rmul__(self, linke_seite):
        return self * linke_seite
    def __abs__(self):
        return math.sqrt(self * self)
    def __repr__(self):
        return "Vektor(%g, %g)" % (self.x, self.y)
    def __str__(self):
        return "(%g, %g)" % (self.x, self.y)

Die Klasse greift in möglichst vielen Methoden auf die Hilfe von anderen Methoden der Klasse zurück. Zum Beispiel subtrahiert die Klasse zwei Vektoren, indem zum ersten Vektor der negierte zweite Vektor hinzukommt. Durch diesen Ansatz sparen Sie viel redundanten Code, wodurch weniger Fehler auftreten.

Die Methode __init__ in den Zeilen 6 bis 9 erlaubt die optionale Angabe der x- und y-Koordinaten. Sind diese nicht vorhanden, setzt die Methode diese auf den Defaultwert 0. Das Zuweisen an self._cls spart weiter unten etwas Schreibarbeit.

Die Methode __pos__ (Zeile 11) liefert eine Kopie des Vektors. Die Verwendung von self._cls bzw. self.__class__ sorgt dafür, dass die Methode, angewandt auf abgeleitete Klassen, wiederum ein Objekt der abgeleiteten Klasse liefert.

Analog zu __pos__ liefert __neg__ (Zeile 14) eine negativ genommene Kopie des Vektors. Dazu wird der Vektor mit -1 multipliziert. Die Methode __add__ (Programmzeile 17) addiert zwei Vektoren. Die Implementation zeigt, dass sich die Summe jeweils aus den Summen der x- und der y-Komponenten ergibt.

Mit __sub__ (Zeile 20) zieht zwei Vektoren voneinander ab. Das geht mit wenig Code: Bei der Addition des ersten Vektor zählt die Methode einfach den Zweite mit umgekehrtem Vorzeichen hinzu. Die eigentliche Logik steckt in dem Methode __add__. Schreiben Sie weniger Code, schleichen sich auch weniger Fehler ein, und es fällt weniger Aufwand beim Testen an.

Die Multiplikationsmethode __mul__ (Zeilen 23 bis 32) kommt in zwei Varianten daher. Mathematisch ist es möglich, einen Vektor mit einem Skalar, also einer einfachen Zahl, zu multiplizieren. Zusätzlich gibt es das so genannte Skalarprodukt, das die Summe der Produkte der x- beziehungsweise y-Komponenten ist.

Um zu unterscheiden, ob die rechte Seite des Multiplikationsoperators ein Skalar oder ein Vektor ist, prüft die Try-Anweisung, ob der Wert ein Attribut x besitzt. Falls nicht, handelt es sich um einen Skalar und die Methode löst einen AttributeError aus. Float-Objekte haben kein Attribut x – Objekte vom Typ Vektor dagegen schon, weil die Klasse sie so definiert.

Die Fehlerbehandlung (Codezeilen 27 bis 29) multipliziert entsprechend die Komponenten des Vektors mit dem Skalar; ein neuer Vektor entsteht. Der Else-Zweig behandelt den Fall zweier Vektoren und gibt das Skalarprodukt zurück.

Die Programmzeilen 34 und 35 zeigen die Methode __rmul__. Diese läuft ab, wenn der andere Operand auf der linken Seite des Multiplikationszeichens steht. Bei der Addition beziehungsweise Subtraktion war das unnötig, weil links immer ein Vektor steht, und damit der richtige Aufruf von __add__ oder __sub__ gesichert ist. Bei der Multiplikation wäre dagegen links auch ein Skalar möglich.

Der Absolutbetrag eines Vektors errechnet sich mit Hilfe des Skalarprodukts des Vektors mit sich selbst (Zeile 37). Dabei hilft die Quadratwurzel-Funktion aus dem Modul math. Die restlichen Methoden der Klasse, __repr__ und __str__, kümmern sich um das Formatieren von Vektor-Objekten, beispielsweise bei der Ausgabe mit print.

Attributzugriff

Weisen Sie einer Variablen ein Objekt zu, verknüpft Python lediglich das Objekt auf der rechten Seite mit dem Namen auf der linken Seite (siehe erster Teil des Kurses). Steht auf der linken Seite dagegen eine Klasse mit Attribut (Klasseninstanz .Attribut ), so besteht die Möglichkeit, den Zugriff abzufangen und besonders zu behandeln. Auf die gleiche Weise beeinflussen Sie lesende Zugriffe auf das Attribut und das Löschen (durch del).

Vor allem bei mehreren Attributen, die Sie ähnlich behandeln möchten, empfiehlt sich der Einsatz der Methoden __getattr__, __setattr__ und __delattr__, um den lesenden, schreibenden oder löschenden Attributzugriff zu steuern. Das Beispiel in Listing 6 demonstriert die Handhabung von Attributzugriffen anhand einfacher Stellvertreter-Objekte, die Attributzugriffe anzeigen und auf das eigentliche Objekt self._obj "umleiten".

Listing 6
class Proxy(object):
    def __init__(self, obj):
        self.__dict__['_obj'] = obj
    def __getattr__(self, name):
        print "Lese Attribut", name
        return getattr(self._obj, name)
    def __setattr__(self, name, wert):
        print "Setze Attribut %s auf %s" % (name, wert)
        setattr(self._obj, name, wert)
    def __delattr__(self, name):
        print "Loesche Attribut", name
        delattr(self._obj, name)
    def __call__(self):
        return self._obj

Der Konstruktor in den Zeilen 2 und 3 setzt das Attribut self._obj direkt über das Objekt-Dictionary self.__dict__. Der Grund für die vordergründig komplizierte Technik folgt weiter unten bei der __setattr__-Methode. Hier sei nur gesagt, dass self.__dict__ den Namensraum des Objekts self als Dictionary zugänglich macht, genau so wie die eingebaute Funktion globals() das für den Namensraum eines Moduls tut.

Die Methode __getattr__ (Zeilen 5 bis 7) kommt immer dann zum Einsatz, wenn das gesuchte Attribut nicht im Suchpfad des Objektnamensraums vorhanden ist. Die Definition gibt mit einer Print-Anweisung aus, welches Attribut Sie angefordert haben, und holt das gewünschte Attribut aus dem enthaltenen Objekt self._obj.

Die Methode __setattr__ (Zeilen 9 bis 11) funktioniert ähnlich: Sie beschreibt mittels einer Print-Anweisung den Attributzugriff und leitet diesen an das Objekt self._obj weiter. Die Methode __setattr__ tritt bei jedem zu setzenden Attribut in Aktion, unabhängig davon, ob es bereits im Namensraum des Objekts vorhanden ist.

Lautete nun die Zuweisung im Konstruktor self._obj = obj, würde __setattr__ aufgerufen. In der Definition in Zeile 11 ruft self._obj implizit __getattr__ auf, worin in Zeile 7 wiederum __getattr__ aufgerufen würde. Dadurch käme es zu einer unendlichen Rekursion. Indem in Zeile 3 der Konstruktor den Namensraum des Objekts direkt verändert, den Aufruf von __setattr__ also umgeht, verhindert er die Rekursion.

Die __delattr__-Methode wiederum ist für das Löschen eines Attributs zuständig. Auch dieser Zugriff wirkt auf das enthaltene Objekt self._obj. Ein Aufruf von __call__ (ab Zeile 17) gibt das gekapselte Objekt zurück. Listing 7 zeigt ein Anwendungsbeispiel für die Proxy-Klasse im interaktiven Interpreter.

Listing 7
>>> import proxy
>>> class C(object):def methode(self, argument):print argument
…
>>> x = C()
>>> p = proxy.Proxy(x)
>>> p.methode(7)
Lese Attribut methode
7
>>> p.a = 1
Setze Attribut a auf 1
>>> print p.a
Lese Attribut a
1
>>> print x.a # direkter Zugriff auf x
1
>>> print p().a # direkter Zugriff auf x
1
>>> del p.a
Loesche Attribut a
>>> print x.a
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: 'C' object has no attribute 'a'

Seit Python 2.2 gibt es außerdem so genannte Deskriptoren [7], die Zugriffe auf einzelne Attribute regeln. Deren Programmierung ist allerdings etwas komplizierter.

Einem Freund empfehlen    Druckansicht Bookmark and Share
Kommentare

Hits
Wertung: 165 Punkte (8 Stimmen)

Schlecht Gut

Infos zur Publikation

Infos zur Publikation

LinuxUser 06/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

Adobe AIR
Adobe-AIR-Programme installieren und (manuell) starten
Tim Schürmann, 14.05.2012 13:09, 0 Kommentare

Es gibt sie noch: neue Anwendungen, die Adobes Integrated Runtime voraussetzen. Aktuellstes und vermutlich auch größtes Beispiel ist das Adventure Botanicula

Aktuelle Fragen

gibt es ein Kommandozeilen Tool, um ein X11-Fenster in ein Anderes einzubetten?
GoaSkin , 21.05.2012 16:44, 0 Antworten
Das XEmbed-Protokoll ist u.A. dazu gedacht, dass man eine X11-Anwendung in eine andere wie ein Wi...
Apache2, Options -Indexes geht nicht
no no, 12.05.2012 19:01, 8 Antworten
Habe in apache2.conf folgendes stehen: Options -Indexes ...
LInux auf Dell LS H500
Andreas Endresl, 09.05.2012 08:54, 2 Antworten
Habe einen alten Dell Latitude LS H500 nur mit ext. Floppy und CD es geht nur immer eines von den...
Datenwiederherstellung unter Ubuntu 12.04 mit "Simple Backup" nach Umzug von Linux Mint
Christian Lottmann, 07.05.2012 13:33, 0 Antworten
Vor dem Umzug auf Ubuntu 12.04 habe ich unter Linux MInt mit "Simple Backup" voll (15.4.2012) und...
DKMS für den propritären NVIDIA-Treiber
Commander Data, 26.04.2012 22:02, 2 Antworten
Hallo an die Gemeinde. Ich habe hier ein interessantes Stück openSuSE gefunden. http://forums.op...