Den eigenen Weg gehen

Objektorientierte Programmierung mit Python

Objektorientierte Programmierung mit Python

Den eigenen Weg gehen

Objektorientierte Programmierung mit Python


Ein wesentlicher Aspekt der Philosophie von Python ist es, Entwickler:innen so viele Freiheiten wie nur möglich zu geben, und auch in der objektorientierten Programmierung geht Python eigene Wege.

Beispielsweise gibt es in Python-Klassen keine echten privaten Member und bei der Einrichtung von Methoden, die sich nicht auf einzelne Instanzen, sondern auf die Klasse selbst beziehen, kann der Entwickler zwischen zwei Varianten wählen. Auf diese und weitere Besonderheiten werden wir in diesem Artikel eingehen.

Instanzattribute und __init__()-Methode

Die __init__()-Methode ist das Gegenstück zum Konstruktor anderer Programmiersprachen. Wie dieser wird sie beim Erzeugen eines Objekts automatisch ausgeführt, im Zusammenhang mit den Instanzattributen („Instanzattribut“ ist in Python die übliche Bezeichnung für eine Member-Variable, also für eine Variable, die zu einer bestimmten Instanz einer Klasse gehört) kommt ihr in Python jedoch eine besondere Bedeutung zu. Instanzattribute werden in Python nämlich ausschließlich innerhalb von Methoden definiert. Außerdem sind Instanzattribute in Python dynamisch, das heißt, sie werden erst dann angelegt, wenn die entsprechende Anweisung ausgeführt wird. Folglich erhält ein Objekt ein Attribut erst dann, wenn die Methode, in der es definiert ist, auf dem Objekt aufgerufen wird. Wenn man also möchte, dass ein Objekt seine Eigenschaften sofort nach der Instanziierung besitzt, dann muss man diese innerhalb der __init__()-Methode definieren.

In der Klasse Rectangle (Listing 1) sind die Attribute length und width in der __init__()-Methode mit dem Standardwert 1 definiert. Eine Anweisung wie rect = Rectangle() erzeugt daher ein Rechteck mit einer Länge von 1 und einer Breite von 1. In Python muss jedes Attribut initialisiert werden, das gilt für Instanzattribute wie für Klassenattribute; eine automatische Wertzuweisung, wie beispielsweise in C# oder Visual Basic, erfolgt nicht. Bei allen Instanzmethoden, also auch bei der __init__()-Methode, ist als erster Parameter ein Verweis auf das aktuelle Objekt anzugeben. Üblicherweise verwendet man hierfür den Bezeichner self. Beim Aufruf übergibt Python self jedoch automatisch. Das heißt, das erste Argument in der Aufrufliste wird automatisch dem zweiten formalen Parameter zugeordnet – der erste ist ja, wie gesagt, self.

Listing 1

class Rectangle:
  def __init__(self, length = 1, width = 1):
    self.length = length
    self.width = width

  def change_length(self, l):
    self.length += l

  def change_width(self, w):
    self.length += w

  def show_area(self):
    print(self.length * self.width)

In Listing 2 folgen ein paar Anweisungen, mit denen sich die Rectangle-Klasse testen lässt. Die Ausgaben der show_area()-Aufrufe zeigen wie erwartet die Werte 1 für das erste Rechteck und 4 für das zweite.

Listing 2

rect1 = Rectangle()
rect2 = Rectangle()

rect2.change_length(3)

rect1.show_area()
rect2.show_area()

Namenskonventionen in Python, PEP 8

Variablen, Klassenattribute, Funktionen und Methoden werden in Python üblicherweise komplett in Kleinbuchstaben geschrieben. Wenn ein Name aus mehreren Wörtern besteht, werden diese mittels Unterstrich getrennt, wie zum Beispiel change_length, show_area oder number_of_participants. Bei Klassennamen bedient man sich dagegen der PascalCase-Notation, der erste Buchstabe wird großgeschrieben und jedes weitere Wort beginnt ebenfalls mit einem Großbuchstaben. Diese Vorgaben entsprechen dem Python Enhancement Proposal 8, kurz PEP 8. Die darin formulierten Regeln und Empfehlungen sind zwar, wie so vieles in Python, nicht zwingend, sie sind aber sehr sinnvoll, weil sie eine einheitliche und gut lesbare Struktur des Quellcodes gewährleisten. Eine Übersicht aller PEPs finden Sie unter [1], die direkte Adresse von PEP 8 (Style Guide for Python Code) finden Sie unter [2].

In Abbildung 1 sehen Sie die Fehlermeldung von PyCharm bei einem Bezeichner, der nicht den PEP-8-Regeln entspricht – hier der Klassenname rectangle (allerdings irrt er sich hier in der Bezeichnung; die Meldung sollte lauten „Class names should use PascalCase convention“; camelCase beginnt mit einem Kleinbuchstaben).

saumweber_python_1

Abb. 1: Der Editor von PyCharm mahnt Bezeichner an, die nicht den PEP-8-Regeln entsprechen

Die Dynamik von Instanzattributen beschränkt sich aber nicht auf den Aufruf von Methoden. Es ist auch möglich, zur Laufzeit einzelne Objekte um Attribute zu erweitern. In der obigen Klasse Rectangle ist zwar eine Methode show_area() definiert, aber Instanzen dieser Klasse besitzen von Haus aus kein Attribut für die Fläche. In Listing 3 wird der Instanz rect2 ein solches Attribut nachträglich hinzugefügt.

Listing 3

rect1 = Rectangle()
rect2 = Rectangle(5, 6)

rect2.area = rect2.length * rect2.width
print(rect2.area) # Ausgabe: 30

Nach Ausführung der Anweisung rect2.area = rect2.length * rect2.width besitzt die Rectangle-Instanz rect2 ein Attribut area und es kann im Weiteren mit diesem genauso verfahren wie mit den Attributen, die in der __init__()-Methode der Klasse definiert sind. Dementsprechend wird das neue Attribut in...