{ "cells": [ { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "# Objektorientierte Programmierung\n", "\n", "Objektorientierte Programmierung (OOP) ist eine der größten Herausforderungen für Beginner, wenn sie lernen, Python zu programmieren. \n", "\n", "Es gibt viele, wirklich viele Übungen und Lektionen die OOP abdecken. Ihr könnt für zusätzliches Material einfach danach googln. Außerdem stelle ich euch einige nützliche Links zu anderen Übungen ans Ende dieses Notebooks.\n", "\n", "Wir werden das Wissen dieser Lektion über OOP auf folgenden Themen aufbauen:\n", "\n", "* Objekte\n", "* Nutzen des class Stichworts\n", "* Erstellen von class Attributen\n", "* Erstellen von Methoden in class\n", "* Lernen über Vererbung\n", "* Spezielle Methoden für classes\n", "\n", "Lasst uns diese Lektion damit beginnen, uns unser Wissen über grundsätzliche Python Objekte in Erinnerung zu rufen. Zum Beispiel:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "l = [1,2,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wisst ihr noch, wie wir Methoden auf eine Liste anwenden?" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l.count(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Was wir diese Lektion hauptsächlich untersuchen werden ist, wie wir ein Objekt wie dieses erstellen können. Wir haben ja bereits gelernt, wie wir Funktionen erstellen können. Lasst uns also zunächst Objekte im Allgemeinen anschauen:\n", "\n", "## Objekte\n", "\n", "In Python ist alles ein Objekt. Erinnert euch daran, dass wir in vorherigen Lektionen type() dazu benutzen, um zu überprüfen, welchen Typ ein Objekt hat:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "\n", "\n" ] } ], "source": [ "print(type(1))\n", "print(type([]))\n", "print(type({}))\n", "print(type(()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wir wissen also, dass all diese Dinge Objekte sind. Wie können wir nun unsere eigenen Objekte erstellen? An dieser Stelle kommt das `class` Stichwort ins Spiel.\n", "\n", "## class\n", "\n", "Die vom Nutzer definierten Objekte werden durch das class Stichwort erstellt. class ist eine Blaupause, die die Eigenschaften eines zukünftigen Objektes festhält. Von Klassen (classes) können wir Instanzen konstruieren. Eine Instanz ist ein spezifiziertes Objekt, das aus einer bestimmten Klasse geschaffen wurde. Zum Beispiel haben wir oben l erstellt, dass eine Instanz eines Listen Objekts ist.\n", "\n", "Lasst uns sehen, wie wir class verwenden:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "# Einen neuen Objekt Typ erstellen, der Beispiel heißt\n", "class Beispiel(object):\n", " pass\n", "\n", "# Instanz von Beispiel\n", "\n", "x = Beispiel()\n", "\n", "print(type(x))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Üblicherweise geben wir Klassen einen Namen, der mit einem Großbuchstaben beginnt. Achtet darauf, wie x jetzt eine Referenz für unsere neue Instanz der Beispiel Klasse ist. \n", "\n", "Innerhalb der Klasse haben wir aktuell nur `pass`. Wir können aber auch Kassen Attribute und Methoden definieren.\n", "\n", "Ein Attribut ist eine Charakteristik eines Objekts. Eine Methode ist eine Operation, die wir auf das Objekt anwenden können.\n", "\n", "Zum Beispiel können wir eine Klasse erstellen, die Hund heißt. Ein Attribut könnte beispielsweise die Rasse oder der Name sein, während eine Methode des Hundes z.B. bellen() sein könnte. \n", "\n", "Lasst uns ein besseres Verständnis dafür durch ein Beispiel erhalten:\n", "\n", "## Attribute\n", "\n", "Die Syntax für ein Attribut ist:\n", " \n", " self.attribut = etwas\n", " \n", "Es gibt eine spezielle Methode namens:\n", "\n", " __init__()\n", " \n", "Diese Methode wird genutzt, um das Attribut eines Objektes zu initiieren. Zum Beispiel:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Hund(object):\n", " def __init__(self,rasse):\n", " self.rasse = rasse\n", " \n", "sam = Hund(rasse=\"Lab\")\n", "frank = Hund(rasse=\"Huskie\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lasst uns herunterbrechen, was wir getan haben. Die spezielle Methode\n", "\n", " __init__()\n", " \n", "wird automatisch aufgerufen, sobald das Objekt erstellt wurde:\n", "\n", " def __init__(self,rasse):\n", " \n", "Jedes Attribut in einer Klassendefinition beginnt mit einer Referenz auf das Instanz-Objekt. Gewöhnlich nennt man es `self` (selbst). Die Rasse ist das Argument. Der Wert wird während der Klassenerstellung übergeben.\n", "\n", " self.rasse = rasse\n", " \n", "Jetzt habebn wir zwei Instanzen der Hunde Klasse erstellt. Mit zwei Rassen. Um diese Attribute aufzurufen können wir nun folgendes tun:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Lab'" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sam.rasse" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Huskie'" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "frank.rasse" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Bemerkenswert ist an dieser Stelle, dass wir keine Klammern hinter rasse verwendet haben. Das liegt daran, dass es ein Attribut ist. Diese nehmen keine Parameter auf.\n", "\n", "In Python gibt es auch Klassen Objekt Attribute. Diese Klassen Objekt Attribute gelten für alle Instanzen dieser Klasse. Zum Beispiel könnten wir für die Klasse Hund ein Attribut namens spezies einführen. Hunde sind ganz unabhängig von ihrer Rasse und ihrem Namen eins immer mit Sicherheit: Säugetiere. Diese Logik setzen wir folgendermaßen um:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Hund(object):\n", " \n", " # Klassen Objekt Attribut\n", " spezies = \"saeugetier\"\n", " \n", " def __init__(self,rasse,name):\n", " self.rasse = rasse\n", " self.name = name" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": true }, "outputs": [], "source": [ "sam = Hund(\"Lab\",\"Sam\")" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Sam'" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sam.name" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wichtig ist, dass das Klassen Objekt Attribut außerhalb aller Methoden in der Klasse definiert wird. Üblicherweise platzieren wir sie vor dem init." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'saeugetier'" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sam.spezies" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Methoden\n", "\n", "Methoden sind Funktionen die innerhalb des Körpers einer Klasse definiert sind. Sie werden genutzt, um Operationen mit den Attributen unserer Objekte durchzuführen. Methoden sind entscheidend im Abkapselungskonzept des OOP Paradigmas. Das ist entscheidend, wenn Verantwortungen im Programmieren verteilt werden, insbesondere in großen Anwendungen.\n", "\n", "Ihr könnt Methoden grundsätzlich so verstehen, dass es Funktionen auf ein Objekt sind, die das Objekt selbst durch das self Argument berücksichtigen.\n", "\n", "Lasst uns als Beispiel eine Kreis Klasse durchgehen:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Der Radius ist: 2\n", "Die Fläche ist: 12.56\n" ] } ], "source": [ "class Kreis(object):\n", " pi = 3.14\n", " \n", " # Ein Kreis hat einen Radius, der per Standard 1 ist\n", " def __init__(self,radius=1):\n", " self.radius = radius\n", " \n", " # Die flaeche Methode berechnet die Fläche. Achtet auf die Verwendung von self.\n", " def flaeche(self):\n", " return self.radius * self.radius * Kreis.pi\n", " \n", " # Methode um den Radius zu definieren\n", " def radiusEinstellen(self,radius):\n", " self.radius = radius\n", " \n", " # Methode um den Radius auszulesen (das selbe wie .radius aufzurufen)\n", " def radiusErhalten(self):\n", " return self.radius\n", " \n", "c = Kreis()\n", "\n", "c.radiusEinstellen(2)\n", "print(\"Der Radius ist:\",c.radiusErhalten())\n", "print(\"Die Fläche ist:\",c.flaeche())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Großartig! Achtet darauf, wie wir die self Notation genutzt haben, um Referenz zu Attributen innerhalb der Methode zu nehmen. Schaut euch noch einmal genau an, wie der obige Code funktioniert.\n", "\n", "## Vererbung\n", "\n", "Vererbung ist ein Weg, um neue Klassen durch bereits existierende Klassen zu erstellen. Die neu erstellten Klassen werden abgeleitete Klassen genannt. Die Klassen die als Referenz genommen werden nennen wir Basisklassen. Wichtige Vorteile von Vererbung sind Codewiederverwertung und die Reduktion von Komplexität im Programm. Die abgeleiteten Klassen überschreiben oder erweitern die Funktionalität der Basisklassen. \n", "\n", "Lasst uns ein Beispiel mit Rückgriff auf unsere Hunde Klasse durchgehen:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Tier(object):\n", " def __init__(self):\n", " print(\"Tier erschaffen\")\n", " \n", " def werBinIch(self):\n", " print(\"Tier\")\n", " \n", " def essen(self):\n", " print(\"Essen\")\n", " \n", "class Hund(Tier):\n", " def __init__(self):\n", " Tier.__init__(self)\n", " print(\"Hund erstellt\")\n", "\n", " def werBinIch(self):\n", " print(\"Hund\")\n", "\n", " def bellen(self):\n", " print(\"Woof!\")" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Tier erschaffen\n", "Hund erstellt\n" ] } ], "source": [ "d = Hund()" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hund\n" ] } ], "source": [ "d.werBinIch()" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Essen\n" ] } ], "source": [ "d.essen()" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Woof!\n" ] } ], "source": [ "d.bellen()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In diesem Beispiel haben wir zwei Klassen: Tier und Hund. Das Tier ist die Basisklasse, der Hund ist die abgeleitete Klasse,\n", "\n", "Die abgeleitete Klasse erbt die Funktionalität der Basisklasse.\n", "\n", "* Zu sehen an der essen() Methode.\n", "\n", "Die abgeleitete Klasse bearbeitet bereits bestehendes Verhalten der Basisklasse.\n", "\n", "* Zu sehen an der werBinIch() Methode.\n", "\n", "Zu guter Letzt erweitert die Funktionalität der Basisklasse.\n", "\n", "* Zu sehen an der bellen() Methode.\n", "\n", "## Spezielle Methoden\n", "\n", "Abschließend können wir über spezielle Methoden sprechen. Klassen in Python können bestimmte Operationen mit speziellen Methoden Namen implementieren. Diese Methoden werden nicht tatsächlich direkt aufgerufen, sondern bei Python interner Syntax. Lasst uns als Beispiel eine Buch Klasse erstellen:" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Buch(object):\n", " def __init__(self, titel, autor, seiten):\n", " print(\"Ein Buch wurde geschrieben!\")\n", " self.titel = titel\n", " self.autor = autor\n", " self.seiten = seiten\n", " \n", " def __str__(self):\n", " return \"Titel:%s , Autor:%s , Seiten:%s\" %(self.titel, self.autor, self.seiten)\n", " \n", " def __len__(self):\n", " return self.seiten\n", " \n", " def __del__(self):\n", " print(\"Ein Buch wurde zerstört!\")" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ein Buch wurde geschrieben!\n", "Ein Buch wurde zerstört!\n", "Titel:Python rockt! , Autor:Dr. Brunner , Seiten:987\n", "987\n", "Ein Buch wurde zerstört!\n" ] } ], "source": [ "buch = Buch(\"Python rockt!\",\"Dr. Brunner\",987)\n", "\n", "# Spezielle Methoden\n", "print(buch)\n", "print(len(buch))\n", "del buch" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Diese speziellen Methoden sind durch Unterstrichte definiert. Sie erlauben es uns, Python spezifische Funktionen auf Objekte anzuwenden, die durch unsere Klasse erstellt werden.\n", "\n", "**Toll! Nach dieser Lektion solltet ihr ein Verständnis dafür haben, wie ihr eure eigenen Objekte mit class erstellen könnt. Ihr werdet davon stark Gebrauch machen, in eurem nächsten Meilenstein Projekt.**" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.1" } }, "nbformat": 4, "nbformat_minor": 2 }