lehrkraefte:blc:informatik:ffprg1-2019:oop-intro

Objektorientierte Programmierung (OOP)

Unter dem Begriff “objektorientierte Programmierung (OOP)” werden ganz verschiedene Charakteristiken zusammengefasst. Wir betrachten im Folgenden nur die Datenkapselung und lassen weitere Aspekte wie Polymorphismus und Vererbung (vorerst) weg.

Eine Grundeigenschaft der OOP ist es, Daten und Funktionen, die diese Daten verarbeiten, in einer Klasse zusammenzufügen.

guetzli.py
class Guetzli:
    def __init__(self, name):
        self.name = name
 
    def say(self):
        print("  --> Ich mag %s" % self.name)
 
    def umtaufen(self, neu):
        self.name = neu
 
 
zimt = Guetzli("Zimtstern")
mailand = Guetzli("Mailaenderli")
 
print("zimt.say():")
zimt.say()
print("mailand.say():")
mailand.say()
 
print("referenz = zimt")
referenz = zimt
print("referenz.say():")
referenz.say()
print("referenz.umtaufen(\"Leckerli\")")
referenz.umtaufen("Leckerli")
print("zimt.say():")
zimt.say()

Eine Klasse bildet die Vorlage, aufgrund welcher Instanzen erzeugt werden können:

zimt = Guetzli("Zimtstern")

Die Klasse ist mit der Ausstechform vergleichbar, mit welcher viele unabhängige Guetzli, d.h. Instanzen erzeugt werden können. Diese Instanzen werden nicht direkt in einer Variablen gespeichert, sondern nur eine Referenz darauf (so quasi wo im Speicher die Information liegt). Die Zeile

referenz = zimt

erzeugt also keine neue Instanz (kein zusätzliches Guetzli), sondern beide Variablen zeigen auf den gleichen Speicherbereich. Eine Instanz zu kopieren ist nicht trivial, weil darin selbst wieder Referenzen auf weitere Instanzen vorhanden sein können.

Die Instanz selbst muss immer als Parameter übergeben werden, der immer self heisst. Die Zeile

referenz.umtaufen("Leckerli")

ist eine Kurzform für

Guetzli.umtaufen(referenz, "Leckerli")

d.h. von der Klasse Guetzli, wird die Methode (so nennt man Funktionen in Klassen) umtaufen aufgerufen, und zwar mit der Referenz auf (hier) das Zimtguetzli.

Eigene Turtle-Klasse

Die Guetzli-Klasse illustriert einige Dinge, ist aber sonst ziemlich sinnfrei. Wir werden hier eine Klasse Turtle entwickeln und dieser einige Tricks beibringen.

myturtle.py
import math
from gpanel import *
 
class Myturtle:
    def __init__(self):
        # Position:
        self.x=0
        self.y=0
        # Winkel
        self.a = 0
        # Schrittweite
        self.l = 10
        move(self.x, self.y)
 
    def forward(self, draw=True):          
        self.x += self.l * math.cos(self.a)
        self.y += self.l * math.sin(self.a)
        if draw:
            lineTo(self.x, self.y)
        else:
            move(self.x, self.y)
 
 
    def turn(self,a):
        self.a+=a/180.0*math.pi
 
 
 
if __name__=="__main__":
    makeGPanel(-20, 20, -20, 20)
    t = Myturtle()
    for i in range(5):
        t.forward()
        t.turn(72)
 

Die Turtle-Klasse hat zur Zeit 4 Instanzvariablen: x,y,a und l, die immer mit z.B. self.x angesprochen werden. Diese können auch direkt manipuliert werden, z.B. mit

t.x=-5

wird die $x$-Koordinate auf $-5$ gesetzt. Das ist aber nicht unbedingt eine gute Praxis. In vielen Fällen schreibt man Methoden, um die Variablen zu setzen und auszulesen. Das hat den Vorteil, dass die Werte überprüft werden können und dass der Zeichenstift auch dorthin bewegt werden kann. Z.B. wie folgt:

    def setX(self, x, constrain=True):
        if constrain:
            if x<toWindowX(0):  # Kleinstmoeglicher x-Wert
                x = toWindowX(0)
            if x>toWindowX(getWindow().width): # Groesstmoeglicher x-Wert
                x = toWindowX(getWindow().width)
        self.x = x
        move(self.x, self.y)

L-Systems

Wir werden jetzt selbst eine ganz einfache Programmiersprache definieren, um die Turtle zu steuern. Die Sprache besteht aus einzelnen Buchstaben, deren Bedeutung wie folgt ist:

  • F mache einen Schritt vorwärts.
  • + drehe im Gegenuhrzeigersinn.
  • - drehe im Uhrzeigersinn.

Wir programmieren eine Methode, die einen String als Parameter entgegennimmt und die Turtle entsprechend steuert:

    def lsyst(self, code):
        for c in code:    # Alle Buchstaben durchgehen
            #
            # Tu was, je nachdem was c ist
            #

Wir werden jetzt den Befehl F jeweils durch den ganzen Code ersetzen (bis zu einer gewissen Tiefe), und so eine Figur zeichnen. Dazu wählen wir einen zusätzlichen Parameter tiefe, der angibt, wie viele Male noch ersetzt werden muss.

            if c=="F":
                if (tiefe==0):
                    self.forward()
                else:
                    self.lsyst(code, tiefe-1);  # Anstatt Zeichnen, den gleichen Code an dieser Stelle ausfuehren
  • ( speichere die aktuellen Parameter (Positition, Richtung, evtl. weitere)
  • ) stelle die zuletzt gespeicherten Parameter wieder her.

Programmieren Sie zwei Methoden push und pop, die die Parameter in einem Array speichern, bzw. von dort wiederherstellen.

Zeichnen Sie dann einen Strauch, z.B. mit “F(+F+F-F)F(-FF+F)F”.

Übergeben Sie der Methode zusätzlich einen Hash (auch Dictionary) genannt, der als Schlüssel Buchstaben und als Einträge weitere Befehle enthält, durch die die Buchstaben ersetzt werden sollen. Ein Aufruf sieht dann wie folgt aus:

t.lsyst("F++F++F", {"F":"F-F++F-F"},2)

was dann eine vollständige Kochschneeflocke zeichnet.

  • lehrkraefte/blc/informatik/ffprg1-2019/oop-intro.txt
  • Last modified: 2019/04/02 11:20
  • by Ivo Blöchliger