Home
Navigation
Impressum
Coder Welten - Programmierung und Optimierung
Coder Welten
 
 

 

 

OOP Python: Von Klassen, Objekten und Instanzen

Objektorientierte Programmierung mit Python 3

Früher oder später sieht sich ein fortgeschrittener Einsteiger auf seinem Weg zum Programmierer mit der objektorientierten Programmierung konfrontiert und bei dem einem oder anderen sehen anfänglich alle Beispiele verwirrend aus. Doch das braucht nicht zu sein und deshalb haben wir einige einfache Beispiele für den leich­teren Einstieg in die objektorientierte Programmierung mit Python 3 zusam­men­gestellt.

Auf das Für und Wider einer objektorientierten Programmierung möchten wir an dieser Stelle nicht eingehen, es geht bis zu einem gewissen Grade auch ohne OOP. Doch wer mit der objektorientierten Programmierung beginnt, sollte sich weitest­gehend an die allgemeinen Spielregeln (Konvention) halten und zu diesen gehört, dass es sich beim Bezeichner einer Klasse um ein Substantiv handeln sollte. Über ein zusammengesetztes Substantiv in der CamelCase-Schreibweise, die bei Python mit CapWords bezeichnet wird, dürfte sich niemand beschweren.
Ob hinter dem Bezeichner der Klasse eine Notation wie die folgende mit (object) folgt, ist hingegen bei neueren Versionen von Python optional, so lange es sich um keine erbende Klasse handelt. Ebenfalls ist ein Doc-String optional, der, insofern einer verwendet wird, eine Kurzbeschreibung der Klasse enthalten soll­te.

class Bezeichner(object):
    """Doc-String mit Kurzbeschreibung der Klasse"""

Bei den Bezeichnern von selbstdefinierten Funktionen sind zwei Schreibweisen weit verbreitet, wobei von den Entwicklern von Python die zweite favorisiert wird.

def camelCase():
def camel_case():

Wenn wir anfänglich auf unseren Seiten die erste Variante bevorzugten, so aus dem Grunde, weil die erste Schreibweise im deutschsprachigen Raum mehr an die natürliche Sprache und Schreibweise angelehnt ist und somit leichter lesbar. Im englischen Sprachraum mag es sich umgekehrt verhalten. Funktional sind sich beide Schreib­weisen gleichgestellt. Bei nachträglichen Überarbeitungen halten wir uns zwischenzeitlich zunehmend an die zweite Variante.
Weiterhin sei erwähnt, für die Verknüpfung von Variablen mit Strings gibt es unter­schiedliche Varianten, wobei die erste Variante mit dem + Operator zwar eine praktische Alternative zur formatierten Ausgabe darstellt, doch die .format() Me­thode letztendlich mehr zu bieten hat, z.B. wenn es um die Formatierung von Zahlen geht. Es schadet zumindest nichts, sich von Anbeginn an mit dieser Me­thode anzufreunden.

variable = "Variablen"

print("Ein String mit einer " + variable + ".")
print("Ein String mit einer {0:s}.".format(variable))

variable = 63

print("Ein String mit einer Summe von {0:4.2f}.".format(variable))
print("Ein String mit einer Summe von {0:03d}.".format(variable))

Ausgabe:

Ein String mit einer Variablen.
Ein String mit einer Variablen.
Ein String mit einer Summe von 63.00.
Ein String mit einer Summe von 063.

Doch kommen wir zur ersten Klasse, diese enthält nur einige Klassen-Variablen. Allgemein werden Variablen innerhalb einer Klasse je nach Sprache auch noch als Attribute, Member oder Eigenschaften bezeichnet. Unterhalb der Klasse sind unter­schiedliche Varianten aufgeführt, um auf diese Klassen-Variablen oder Ei­gen­schaf­ten zuzugreifen und zu verwenden. Die Bezeichnung Attribute ist nicht nur in Python gebräuchlich und nebebei bemerkt, eigentlich wird jeder Wert mit einem Para­meter an eine Funktion oder Methode übergeben und innerhalb einer Funktion oder Methode als Argument benutzt.

Erste Klasse:

# Erstellung und Definition der Klasse Mensch

class Mensch:
    """Der evolutionäre Stammbaum des Menschen als Klasse"""

    ordnung  = "Primaten"
    teilordg = "Altweltaffen"
    ueberfam = "Menschenartige"
    familie  = "Menschenaffen"
    tribus   = "Hominini"
    gattung  = "Homo"
    spezies  = "Mensch"

# Instanziieren von Objekten der Klasse Mensch

# Variante 1:
print(Mensch.ordnung)

# Variante 2:
print(Mensch().tribus)

# Variante 3:
stammbaum = Mensch()

print("Der {0} gehört zur Familie der {1}.\n".format(
      stammbaum.spezies, stammbaum.familie))

Ausgabe:

Primaten
Hominini
Der Mensch gehört zur Familie der Menschenaffen.

Bei der ersten Klasse handelt es sich bereits um eine richtige Klasse, von der Instanzen als Vertreter dieser Klasse erzeugt wurden. Dennoch würde deren Einsatz noch nicht allzu viel Sinn ergeben, um gleich alles auf OOP umzustellen und nur noch objektorientiert zu programmieren, da noch keine Methoden ent­halten sind, die etwas bewirken könnten. Auch sollten Klassen erst dann benutzt werden, wenn einfache Funktionen nicht mehr genügen. Eher ist es üblich, aus Klassen ebenfalls auf Funktionen als Gehilfen zurückzugreifen.
Bei Methoden handelt es sich lediglich um einen anderen Namen für Funktionen, da jede selbstdefinierte Funktion innerhalb einer Klasse als Methode bezeichnet wird.

Zweite Klasse:

# Erstellung und Definition der Klasse Systematik

class Systematik:
    """
    Klasse der Systematik des evolutionären Stammbaums des Menschen
    """
    klasse   = "Säugetiere"
    ordnung  = "Primaten"
    teilordg = "Altweltaffen"
    ueberfam = "Menschenartigen"
    familie  = "Menschenaffen"
    tribus   = "Hominini"
    gattung  = "Homo"
    spezies  = "Mensch"

    def ordne_spezies():
        ausgabe  = "Die Spezies Mensch gehört zur Überfamilie der "
        ausgabe += Systematik.ueberfam + "."
        print(ausgabe)

    def ordne_familie():
        return "Die Überfamilie der {0} gehört " \
               "zur Klasse der {1}.\n".format(
               Systematik.ueberfam, Systematik.klasse)

# Instanziieren von Objekten der Klasse Systematik

# Variante 1:
Systematik.ordne_spezies()

# Variante 2:
stammbaum = Systematik
print(stammbaum.ordne_familie())

Ausgabe:

Die Spezies Mensch gehört zur Überfamilie der Menschenartigen.
Die Überfamilie der Menschenartigen gehört zur Klasse der Säugetiere.

Die zweite Klasse enthält bereits zwei Methoden, die jeweils eine Funktion erfüllen. Was jedoch noch fehlt, ist eine einfache Möglichkeit, mit der sich diese Funktionen nach eigenen Vorstellungen oder entsprechend eines Programm­ablaufs wenigstens etwas steuern lassen.

Dritte Klasse:

# Erstellung und Definition der Klasse Eigenschaften

class Eigenschaften:
    """Klasse mit menschlichen Eigenschaften"""

    # Klassen-Variablen/Eigenschaften
    eigenheit = "spendabel"
    charakter = "aufrichtig"

    def setze_eigenschaft(wert):
        Eigenschaften.eigenheit = wert

    def liefere_eigenschaft():
        return "Einige Menschen sind {}".format(Eigenschaften.eigenheit)

# Instanziieren von Objekten der Klasse Eigenschaften

# Erster Instanz
erste_instanz = Eigenschaften
erste_instanz.setze_eigenschaft("gierig")
print(erste_instanz.liefere_eigenschaft())

# Zweite Instanz
zweite_instanz = Eigenschaften
print(zweite_instanz.liefere_eigenschaft())

Ausgabe:

Erster Instanz:
Einige Menschen sind gierig

Zweite Instanz:
Einige Menschen sind gierig

Die dritte Klasse enthält bereits eine Methode, die ein Programmierer als kleine Schnittelle benutzen könnte, um die vorgegebenen menschlichen Eigenschaften innerhalb der Klasse durch neue Werte zu verändern und dadurch die Antwort der zweiten Methode zu beeinflussen. Doch einmal gesetzte oder veränderte Eigen­schaften von Klassen-Variablen bleiben für alle nachfolgenden Instanzen für die Laufzeit einer Anwendung gleich, bis diesen Eigenschaften ein neuer Wert zuge­wiesen wird.
Klassen-Variablen werden aus diesem Grund auch als statische Eigenschaften bezeichnet. Je nach Einsatz können statische Eigenschaften erwünscht sein, doch oftmals sind sie es nicht.

Vierte Klasse:

# Erstellung und Definition der Klasse Charaktere

class Charaktere:
    """Klasse mit menschlichen Charaktereigenschaften"""

    def __init__(self, eigenheit="spendabel", charakter="aufrichtig"):
        # Instanz-Variablen / Attribute - Eigenschaften
        self.eigenheit = eigenheit
        self.charakter = charakter

    def liefere_eigenschaft(self):
        return "Einige Menschen sind {0}".format(self.eigenheit)

# Instanziieren von Objekten der Klasse Charaktere

# Erster Instanz
erste_instanz = Charaktere("gierig")
print(erste_instanz.liefere_eigenschaft())

# Zweite Instanz
zweite_instanz = Charaktere()
print(zweite_instanz.liefere_eigenschaft())

Ausgabe:

Erster Instanz:
Einige Menschen sind gierig

Zweite Instanz:
Einige Menschen sind spendabel

Um nicht nur mit statischen Eigenschaften zu arbeiten, werden in der vierten Klasse die gewünschten Eigenschaften nicht als Klassen-Variablen, sondern als Instanz-Variablen mit Hilfe der Initialisierungsmethode __init__ angelegt. Die Initialisierungsmethode wird oftmals mit einer Konstruktor-Methode gleichge­setzt, dennoch ist es nur eine Initialisierungsmethode, die jedoch, falls vorhan­den, auto­matisch mit aufgerufen wird, wenn ein neues Objekt einer Klasse wie im Beispiel mit

variable = KlassenBezeicher()

instanziiert wird. Wer nun die Ausgaben von den Instanzen der dritten der vierten Klasse vergleicht, wird den Unterschied erkennen. Die in erster Instanz ver­än­derte Eigenschaft hatte bei der vierten Klasse keine Auswirkungen auf das Ergebnis der zweiten Instanz, nur die Schreibweise ist gewöhnungsbedürftig. Dazu sollte ein Programmierer wissen, der Parameter "self" entspricht einer Referenz auf das instanzi­ierte Objekt, wodurch die Methode und alle Eigen­schaften, die mit "self" verknüpft werden, ebenfalls mit der Instanz verknüpft wer­den.
Falls der eine oder andere Leser an dieser Stelle mit einem Verständnisproblem zu kämpfen hat, ein kleiner Tipp. In der Regel werden im Quelltext einer Webseite auch keine Images eingebunden, sondern nur deren Adressen als Referenz auf eine Quelle. Ein Browser erkennt diese Referenz, ladet die refe­renzierten Imagedateien und verbindet die Images mit der Webseite.
Noch etwas gewöhnungsbedürftiger wird es, wenn Eigenschaften als Klassen-Variablen und als Instanz-Variablen in einer Klasse vereint werden, um beide in den Programmlauf mit einzubeziehen, wie im nächsten Beispiel.
Es sei angemerkt, in beiden Beispielen wurden mehr Parameter mit einem Default-Wert vorbelegt, als für die Beispiele erforderlich gewesen wären. Dabei ging es mehr um die Schreibweise, als um einen praktischen Zweck.

# Erstellung und Definition der Klasse Tribus

class Tribus:
    """Klasse mit einer Zuordnung zum Tribus"""

    # Klassen-Variablen / Attribute - Eigenschaften
    vorname  = "Name"
    nachname = "Nachname"
    standard = "Standard"

    def __init__(self, zugehoerigleit="default", einordnung="tierisch"):
        # Instanz-Variablen / Attribute - Eigenschaften
        self.zugehoerigleit = zugehoerigleit
        self.einordnung = einordnung

    def setze_klassenattribute(self, value01, value02):
        Tribus.vorname = value01
        Tribus.nachname = value02

    def liefere_klassenattribute(self):
        if self.vorname is "John":
            return "{0} {1} entspricht dem männlichen {2}.".format(
                    self.vorname, self.nachname, self.standard)
        elif self.vorname is "Jane":
            return "{0} {1} entspricht dem weiblichen {2}.".format(
                    self.vorname, self.nachname, self.standard)
        else:
            return "Nicht eingeplant!\n"

    def liefere_instanzattribute(self):
        return "Beide sind von {0:s}er Natur.\n".format(
                self.zugehoerigleit) 

    # Nur für eine Kontrolle der Eigenschaften und Werte.
    def kontrolliere_eigenschaften(self):
        return "Klassen-Variable: {0:s}\n" \
               "Klassen-Variable: {1:s}\n" \
               "Instanz-Variable: {2:s}\n" \
               "Instanz-Variable: {3:s}\n".format(
               self.vorname, self.nachname, 
               self.standard, self.zugehoerigleit)
        

# Instanziieren von Objekten der Klasse Eigenschaften

# Erster Instanz
erste_instanz = Tribus("menschlich")
erste_instanz.setze_klassenattribute("John", "Doe")
print(erste_instanz.liefere_klassenattribute())

erste_instanz.setze_klassenattribute("Jane", "Doe")
print(erste_instanz.liefere_klassenattribute())
print(erste_instanz.liefere_instanzattribute())

# Zweite Instanz
zweite_instanz = Tribus()
print(zweite_instanz.kontrolliere_eigenschaften())

Ausgabe:

Erster Instanz:
John Doe entspricht dem männlichen Standard.
Jane Doe entspricht dem weiblichen Standard.
Beide sind von menschlicher Natur.

Zweite Instanz:
Klassen-Variable: Jane
Klassen-Variable: Doe
Klassen-Variable: Standard
Instanz-Variable: default

In der ersten Instanz werden mit Hilfe der Methode "setze_klassenattribute" den Klassen-Eigenschaften neue Werte zugewiesen. Wichtig ist dabei, dass die Klassen-Variablen mit der Klasse verknüpft bleiben und nicht über self mit den instan­ziierten Objekten verknüpft werden.

Muster:

KlassenBezeicher.eigenschaft = neuer Wert

Erst im weiteren Verlauf können diese dann im Zusammenspiel mit self benutzt werden, insofern keine anderen Gründe dagegen sprechen. Im Beispiel waren wie erwartet, die den Klassen-Variablen in erster Instanz zugewiesenen Werte auch in zweiter Instanz verfügbar. Die Instanz-Variable nahm hingegen, ebenfalls wie erwartet, wieder ihren Default-Wert an.

Weiterlesen » Vererbung und Sichtbarkeit

 
Navigation

Einstieg in Python

 


OOP mit Python

  • Klassen und Instanzen

 


Codes & Tutorials

 

Weitere Themen

Übersicht

 


Copyright © Verlag Horst Müller - Stendal - 2006 - Impressum - Datenschutz - Nutzungsbedingungen