Klassen und Objekte


Sensoren am Ergometer

Quelle


🎯 Lernziele

Nach dieser Einheit sind Sie in der Lage dazu * Klassen und Objekte im Sinne der Objektorientierten Programmierung zu beschreiben * Klassen mit Attributen und Methoden in Python anzulegen


Objekte

  • Objektorientierte Programmierung: Programmierparadigma mit Fokus auf Objekte, deren Eigenschaften und Fähigkeiten
  • z.B. Objekt der Klasse Sensor
  • Attribute (Eigenschaften):
    • Zählernummer
    • Aktueller Messwert
    • Liste bisheriger Messwerte
  • Methoden:
    • Mittelwert bilden
    • Messen
    • Letzten Messwert übermitteln

bg right:40% w:400


Klassen

  • Die Klasse Sensor ist eine abstrakte Beschreibung aller denkbaren Objekte vom Typ Sensor
  • Ein konkretes Objekt der Sensor ist eine Instanz (Ausprägung) der Klasse Sensor

bg right:40% w:400


Objektorientierte Programmierung

  • Aufbauend auf strukturierter Programmierung (if-else, for, function())
  • Fokus erst auf das was (Objekte) dann erst auf das wie (Algorithmen, Funktionen, Datenstrukturen)
  • Erleichtert den Einsatz von komplexem Code und Austausch zwischen Entwickler:innen und Anwender:innen

Objekte als Instanzen von Klassen

w:1000

  • Objekte sind Instanzen (Ausprägungen) einer Klasse
  • Eine Klasse beschreibt die Eigenschaften (Attribute) und die Tätigkeiten (Methoden), die ein Objekt der Klasse später besitzen kann.

Attribute und Methoden

w:1000

  • Die Attribute sind Variablen entweder mit elementaren Datentypen (int, str, float) oder wiederum Klassen
  • Die Methoden sind (kleine) Algorithmen, die im Sinne der strukturierten Programmierung implementiert werden müssen (Prozeduren und Funktionen)

Umsetzung in Code

  • Eine Klasse ist das allgemeine Beschreibung einer Klasse von Objekten:

    # Python
    class Pferd():
        # Definition des Konstruktors = Beschreibung der Klasse
        def __init__(self, groesse, hoechstgeschwindigkeit):
            self.groesse = groesse
            self.hoechstgeschwindigkeit = hoechstgeschwindigkeit
            self.geschwindigkeit = 0
    

  • Die Methode __init__ ist der Konstruktor und wird aufgerufen, sobald ein Objekt einer Klasse instanziiert wird

  • Jeder Methode eines Objektes wird der Parameter selfübergeben, welcher aus das Objekt selbst referenziert
  • Hier wird beispielsweise ein Objekt mit den Attributen groesse und geschwindigkeit erzeugt.

Objekte und Klassen 🧠

  • Ein Objekt ist eine spezielle Ausprägung einer Klasse
  • Um mit einer Klasse zu arbeiten werden Objekte der Klasse instanziiert

    # Aufruf des Konstruktors = Instanziierung des Objekts
    black = Pferd("140 cm", '30 kmh')
    beauty = Pferd("150 cm", '40 kmh')
    

  • Die Klasse Pferd beschreibt allgemein das Verhalten jedes Pferdes!

  • Hier werden zwei Objekte der Klasse Pferd angelegt
  • Die beiden Pferde sind durch die Menge ihrer Attribute vollständig beschrieben

Methoden

  • Prozeduren oder Funktionen einer Klasse
# Python
class Pferd():
# [...]

    def laufen(self):
        print("Läuft mit {}".format(self.geschwidigkeit))
        # [...]

black = Pferd("140 cm", '30 km/h')
black.geschwindigkeit = '20 km/h'
black.laufen()
#> "Läuft mit 20 km/h"

Methoden & Funktionen

  • Methoden sind Funktionen, die zu einer Klasse gehören
  • Funktionen sind wieder aufrufbare Code-Einheiten, denen Argumente als Parameter übergeben werden können
  • Funktionen geben einen Rückgabewert aus, der weiterverarbeitet werden kann

bg right:40% w:400


---
title:
---
classDiagram
class Sensor{
        -int sensor_id
        -float letzter_messwert
        -string sensor_name
        -[float] messwerte
        -bilde_mittelwert()
        -messe()
        -uebermittle_messwert()
    }

Quelle


# Python
def bilde_mittelwert(list_of_measurements):
  """ Eine Funktion, die eine Liste von Werten übernimmt und das arithmetische Mittel zurück gibt"""

  mittelwert = sum(list_of_measurements) / len(list_of_measurements)
  return mittelwert

ergebnis = bilde_mittelwert([1,2,3])
# Python
def <funktions_name>(<parameter>):
  """<Docstring: Kommentar zur Dokumentation>"""

  <Berechnungen der Funktion>
  return <Rückgabewert der Funktion>


Beispiel-Klasse Sensor

# Python
# Beschreibung einer Klasse as Python Code
class Sensor():

  # Definition des Konstruktors, welcher ein Objekt der Klasse erzeugen kann
  def __init__(self, sensor_id):
    # Festlegung der Attribute
    self.sensor_id = sensor_id
    self.list_of_measurements = []

  # Definition einer Methode
  def bilde_mittelwert(self):
    return sum(self.list_of_measurements) / len(self.list_of_measurements)

  def messe(self, wert):
    messwert = wert # wir nehmen an, der kommt irgendwo her
    self.list_of_measurements.append(messwert)
    return messwert

# Python
# Anlegen eines Objektes der Klasse Smart Meter
hr_sensor_1= Sensor(sensor_id = 123)

# Aufruf des Attributes meter_id des Objektes hr_sensor_1
print(hr_sensor_1.sensor_id)
# > 123

# Aufruf der Funktion messe des Objektes hr_sensor_1
hr_sensor_1.messe(2)
# Dem Attribut list_of_measurements wurde nun ein Element hinzugefügt
print(hr_sensor_1.list_of_measurements)
# > [2]

hr_sensor_1.messe(4)
print(hr_sensor_1.list_of_measurements)
# > [2,4]

print(hr_sensor_1.bilde_mittelwert())
# > 3.0

Zugriff auf Attribute

  • <object>.<attribute>
    #Python
    print(hr_sensor_1.sensor_id)
    # > 123
    
  • Objekte leben im Arbeitsspeicher

Kapselung (Geheimnisprinzip)

  • Nicht alle Eigenschaften und Methoden eines Objektes müssen/sollten von außen sichtbar oder aufrufbar sein
  • UML erlaubt die Modellierung folgender Zugriffsarten durch ein Zeichen vor dem Attributs- oder Methodennamen:
    • + public: Zugreifbar für alle (anderen) Objekte,
    • - private: Nur für Objekte der eigenen Klasse zugreifbar,

bg w:400 left:33%


---
title:
---
classDiagram
class Subject{
        + string  name
        - int __age
        - __print_new_subject()
    }

Quelle


Beispiel Kapselung

  • Das Alter der Versuchsperson soll von außen nicht sichtbar oder veränderbar sein. Entsprechend werden das Attribut __age und die Methode __print_new_subject() nach außen versteckt.
  • in Python geschieht dies über zwei vorangestellte _
class Subject():
    def __init__(self, name, age):
        self.name = name
        self.__age = age

    def print_subject(self):
      print("Created new subject {} with age {}!".format(self.name, self.__age) )


subject_1 = Subject("Bob", 35)
#> Created new subject Bob with age 35!
# subject_1.__age --> 'subject' object has no attribute '__age'

getter and setter

  • sind Methoden, wie den Zugriff auf gekapselte Attribute ermöglichen
  • ermöglicht Aufteilung zwischen read-only (nur getter) and write-only (nur getter)
  • in anderen Programmiersprachen sehr üblich
class Subject():

    def __init__(self,name, age):
        self.__age = age
    # Getter
    def get_age(self):
      return self.age
    # Setter
    def set_age(self, new_age):
      self.age = new_age

subject_1 = subject("Bob", 35)
print(subject_1.get_age())

Sinnvolle Klassen

Kandidaten für Objekte und Klassen in der objektorientierten Analyse sind: - Ein Ding, das sichtbar und beschreibbar ist - Reale Dinge: Sensor, Ergometer, Fahrzeug... - Etwas, das man sich vorstellen kann - Rollen: Diagnostiker:in, Administrator:in, ... - Spezifikationen: Stufentest, Mittelwert... - Etwas, das in Taten umgesetzt werden kann oder etwas nicht Gegenständliches - Steuerungslogik, ...


✍️ Freiwillige Übungs-Aufgabe

2.1 Objektorientierung


🎯 Lernfortschritt

Nach dieser Einheit sind Sie in der Lage dazu * Klassen und Objekte im Sinne der Objektorientierten Programmierung zu beschreiben * Klassen mit Attributen und Methoden in Python anzulegen


🏆 Sakai-Aufgabe 6.1: Objektorientierung

  • nutzen Sie das bestehende Repository aus Sakai-Aufgabe 3 & 5, um die Aufgabe zu bearbeiten. Damit Sie nun wieder unabhängig voneinander arbeiten können, erstellen Sie jeweils einen neuen Branch mit Ihrem Vornamen und arbeiten Sie alleine in diesem weiter.
  • Committen Sie die stabile Lösungen und comitten Sie wieder mit der Commit-Message Aufgabe61, wenn Sie den Code umgeschrieben haben.
  • erstellen Sie eine neue Datei my_classes.py in der Sie die Klassen Subject, Supervisor und Experiment anlegen. Diese haben jeweils einen Konstruktur (__init__()), um die Attribute zu setzen, die bisher im Dictionary gespeichert wurden.
  • Das Subject enthält zudem eine Methode estimate_max_hr() basierend auf der Funktion.
  • Da wir nicht für jeden Testfall die Daten von Hand eingeben wollen, eine Kopie der main.py namens test.py anlegen (Beispiel, wie das aussehen könnte, finden Sie unten)
  • Geben Sie wieder den Link zum Repository in Sakai ab

Vorschlag für test.py

from my_classes import Subject, Supervisor, Experiment

if __name__ == "__main__":

    # Erstellen eines Leistungstests
    supervisor = Supervisor("FirstName", "LastName")
    subject = Subject("FirstName", "LastName", "female", 30)
    subject.estimate_max_hr()

    experiment = Experiment("Leistungstest", "2021-01-01")
    experiment.add_subject(subject)
    experiment.add_supervisor(supervisor)


    print(experiment)

Startpunkt für my_classes.py

class Subject():
    def __init__(self, first_name, last, sex):
      pass

    def estimate_max_hr(self):
      """A function that estimates the maximum heart rate of a subject"""
      pass

class Supervisor():
    def __init__(self, first_name, last_name):
      pass

class Experiment():
    def __init__(self, name, date):
      pass

    def add_subject(self, subject):
      self.subject = subject

    def add_supervisor(self, supervisor):
      pass