{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Konzept 6: Typen und Objekte\n", "\n", "Ein Computerprogramm verarbeitet Informationen,\n", "welche zur besseren Übersicht benannt und strukturiert werden können.\n", "Angelehnt an das \"echten Leben\" gibt es diverse Möglichkeiten,\n", "dem Programmcode verständlicher zu gestalten.\n", "\n", "Beispielsweise gibt es Kategorisierungen von realen Objekten,\n", "wie \"Kugelschreiber\" oder \"Blatt Papier\".\n", "Auch gibt es jeweils konkrete Ausprägungen solcher Kategorien,\n", "also, zum Beispiel einen bestimmten Kugelschreiber und ein bestimmtes Blatt Papier.\n", "Diese Objekte können dann spezielle Attribute haben,\n", "z.B. eine Farbe, eine Form, eine Größe, eine Länge, etc.,\n", "welche für das jeweilige Objekt spezifisch festgelegt sind.\n", "\n", "Darüber hinaus gibt es auch Interaktionen zwischen Objekten,\n", "also, in unserem Beispiel kann ein bestimmter Kugelschreiber auf ein Blatt Papier \"schreiben\".\n", "Dies ist dann eine Aktion, die ein Kugelschreiber auf ein Objekt der Kategorie \"Papier\" ausführen kann.\n", "All diese Elemente haben parallelen in Python:\n", "\n", "Alle Mitspieler in einem Programm sind Objekte.\n", "Diese beinhalten die gesamte zur Verarbeitung bereitstehende Information des Programmes.\n", "Schon bekannte Vertreter sind: Zahlen, Strings, Listen, Dictionaries, Funktionen, Module, etc.\n", "So ein Objekt ist entsprechend der zum Typen des Objekt's gehörenden Datenstruktur aufgebaut.\n", "Jeder Typ hat einen Namen. Dadurch wird wird es leichter verständlich,\n", "wozu die Datenstruktur dient und was sie repräsentieren soll.\n", "Bei der Instanzierung so einer vorgegebenen Struktur wird sie mit entsprechenden Daten befüllt,\n", "die für das jeweilige konkrete Objekt spezifisch sind.\n", "\n", "Eine \"Klasse\" ist eine Blaupause für Typen und definiert dessen Struktur und Funktionsweise.\n", "Bei der \"Instanzierung\" einer Klasse werden Attribute (das sind Variablen, die dieser Datenstruktur angehören)\n", "nach vorgegebenen Regeln mit Daten befüllt,\n", "oder die in dieser Struktur definierten Objekte übernommen.\n", "Man spricht dann von einem \"Objekt\", welches aus dieser Klasse instanziert wurde.\n", "\n", "Beispielsweise kann ein Typ \"`Dreieck`\" für das mathematische Konzept eines Dreickes stehen.\n", "Eine Instanz davon ist ein Objekt und besteht aus den drei konkreten Eckpunkten des Dreiecks.\n", "\n", "Die Objekte, die bereits bei der Definition der Klasse deklariert wurden,\n", "sind für alle Instanzen der Klasse identisch.\n", "Insbesondere handelt es sich hier üblicherweise um Funktionen,\n", "die fester Bestandteil dieses Types sind.\n", "Diese Funktionen werden \"Methoden\" genannt.\n", "Beim Aufruf so einer assoziierten Methode wird automatisch die jeweilige Instanz als erstes Argument übergeben.\n", "\n", "In der Praxis läuft es üblicherweise so ab,\n", "dass die Argumente bei einer Instanzierung eines Objektes vorverarbeitet werden\n", "und dann als \"Attribute\" des neu erzeugten Objekts abgespeichert werden.\n", "Die dafür zuständige Methode hat den speziellen Namen `__init__(self, ...)`.\n", "\n", "Der Zugriff auf die Attribute des Objekts geschieht mittels \"Punktnotation\", d.h. `.`.\n", "\n", "Das Ziel solch einer Sammlung von Daten und Methoden in einer Klasse ist eine Modularisierung des gesamten Programms.\n", "Dadurch lassen sich diese zusammengehörenden Elemente gemeinsam verwalten und verwenden.\n", "Die Methoden sind hierbei für alle Instanzen eines Typs gleich.\n", "Daher wirken sich Änderungen an einer Methode auf alle späteren Instanzen einer Klasse aus." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zusammenfassung:\n", "\n", "* **Typ**: eine Datenstruktur, die einen Namen trägt\n", "* **Objekt**: ein konkreter Datensatz, der die Struktur eines Typs hat\n", "* **Klasse**: eine Technik, um eine (assoziative) Datenstruktur eines Typs zu definieren\n", "* **Instanz**: ein Objekt, das aus den strukturellen Vorgaben einer Klasse erzeugt wurde\n", "* **Attribut**: die einzelnen Datenelemente so einer Klasse bzw. Objektes; Zugriff mittels Punktnotation\n", "* **Methode**: ein Attribut, das eine Funktion ist; beim Aufruf der Methode wird automatisch die jeweilige Instanz als erstes Argument übergeben.\n", "* **Initialisierung**: der erste automatische Aufruf der Methode `__init__(self, ...)` nachdem ein Objekt erzeugt wurde" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/svg+xml": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " image/svg+xml\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " Class/Type\n", " Instances/Objects\n", " \n", "" ], "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.display import SVG\n", "SVG(filename = \"res/oop1.svg\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Spezialfälle:\n", "\n", "* **Klassenmethoden**: Das ist eine Methode, die anstatt des jeweiligen Objekts, die Klasse selbst als erstes Argument übergeben bekommt.\n", "* **statische Methoden**: Das sind Methoden, die kein erstes Argument automatisch übergeben bekommen. Ihr Verhalten ähnelt jenem von Funktionen." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Beispiele für Typen und Klassen\n", "\n", "* Zahlen, z.B. das Konzept \"Ganzzahl\" wird z.B. durch den Typ `int` realisiert und `42` ist ein Beispiel für ein Objekt dieses Typs.\n", "* Zeichenketten: `basestring` ist das übergeordnete Konzept, welches z.B. der Typ `str` sein kann. Ein Beispiel ist `\"Hase\"`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Den Typ eines Objekts erfährt man mittels des eingebauten Befehls `type`:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "int" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(42)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "str" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(\"Hase\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ein komplexerer Typ ist zum Beispiel das aktuelle **Datum mit der aktuellen Uhrzeit**.\n", "Es wird ein Objekt des Typs \"`datetime.datetime`\" erzeugt, indem der Konstruktor mit ensprechenden Werten befüllt wird.\n", "Anschließend wird mit \"`.hour`\" und \"`.minute`\" die darin enthaltene Stunde und Minute durch die Punktnotation abgefragt." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "datetime.datetime(2014, 10, 11, 12, 13)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from datetime import datetime\n", "earlier = datetime(2014, 10, 11, 12, 13)\n", "earlier" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "12" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "earlier.hour" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "13" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "earlier.minute" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "datetime.datetime" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(earlier)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Jetzt (also zu einer späteren Zeit) wird das aktuelle Datum abgefragt.\n", "Dies geschieht mittels der `.now()` Methode.\n", "\n", "\"`now`\" verweist nun auf das zweite \"`datetime`\" Objekt, mit der aktuellen Uhrzeit." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "datetime.datetime(2016, 5, 21, 15, 40, 18, 840006)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "now = datetime.now()\n", "now" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nun wird die Differenz gebildet, welches ein Objekt des Typs \"`datetime.timedelta`\" ist." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "datetime.timedelta(588, 12438, 840006)" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "timediff = now - earlier\n", "timediff" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "588" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "timediff.days" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "50815638.840006" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "timediff.total_seconds()" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "datetime.timedelta" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(timediff)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Bemerkung:* Ein Tipp aus der Praxis ist, Zeitabfragen prinzipiell immer auf die UTC-Zeit umzurechnen.\n", "`datetime.utcnow()` liefert hier gleich die passend umgerechnete Zeit. Warum ist das besser?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Klassen\n", "\n", "Aus einfachen \"Basistypen\" (`str`, `int`, `float`, `list`, ...) werden komplexeren Typen aufgebaut.\n", "Die Beschreibung deren Struktur sind die **\"Klassen\"** (engl. \"class\").\n", "\n", "Eine Klasse wird auf die Art definiert,\n", "dass zuerst das Schlüsselwort `class` mit dem Namen der Klasse steht.\n", "Die anschließenden Klammern geben an, ob die Klasse von einer anderen abgeleitet wird -- \n", "ist das nicht der Fall, dann gibt man das Basisobjekt `object` an.\n", "\n", "Dann folgen, eingerückt, die Attribute und Methoden der Klasse." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Im folgenden Beispiel wird mittels einer Klasse der Typ `Studierende` definiert,\n", "welcher für das Konzept eines einzelnen Studierenden steht.\n", "Die Attribute dieser Klasse sind `name`, `mtrnr` und `stkz`\n", "und nennt man auch Klassenvariablen, weil sie für diese eine bestimmte Klasse gelten." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Studierende(object):\n", " name = \"Fritz\"\n", " mtrnr = \"0105467\"\n", " stkz = 512" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Die drei Attribute dieser Klasse können mittels Punktnotation abgefragt werden:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'Fritz'" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Studierende.name" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "512" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Studierende.stkz" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Es ist auch jederzeit möglich, die Werte der Klassenattribute zu ändern oder neue Attribute hinzuzufügen:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [], "source": [ "Studierende.name = \"Susi\"" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'Susi'" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Studierende.name" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'purpur'" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Studierende.lieblinsfarbe = \"purpur\"\n", "Studierende.lieblinsfarbe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dies ist aber nur der erste Schritt, die Daten in einem Programm zu organisieren!\n", "\n", "*Die Quintessenz einer Klasse ist jedoch,\n", "mehr als nur eine konkrete Zusammensetzung dieser Attribute und Methoden zu haben.*\n", "\n", "Man möchte gleichzeitig mehrere Studierende erzeugen können,\n", "die zum Beispiel in den Variablen `s1`, `s2`, ... , abgelegt werden\n", "und jeweils in deren Attributen die Namen, die Matrikelnummern und Studienkennzahlen abgespeichert werden.\n", "\n", "## Instanzen von Klassen\n", "\n", "Eine **Instanz** einer Klasse wird dadurch erzeugt, dass sie wie eine Funktion aufgerufen wird.\n", "Üblicherweise möchte man beim Instanzieren auch **Instanzvariablen** setzen,\n", "welche für die jeweilige Instanz spezifisch sind.\n", "Hierfür gibt es in Python eine besondere Methode, \n", "die Inititalisierungsmethode `__init__(self, *args, **kwargs)`,\n", "welche zum automatischen Setzen von Attributen verwendet wird.\n", "Sie wird auch \"Konstruktor\" genannt.\n", "\n", "Zusammengefasst, diese Instanzvariablen sind dann Attribute der Instanz und beinhalten dann\n", "z.B. die für einen bestimmten Studierenden spezifischen Daten\n", "in einer konkreten Instanz einer Klasse -- \n", "also nicht mehr in dem allgemeinen Klassenobjekt, welches für alle Instanzen existiert.\n", "\n", "Das \"Instanzieren\" einer Klasse ist der Geburtsvorgang eines Objekts dieser Klasse." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Definition einer zweiten Version der Klasse\n", "class Studierende2(object):\n", " def __init__(self, name, mtrnr, stkz):\n", " self.name = name\n", " self.mtrnr = mtrnr\n", " self.stkz = stkz" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instanzierung zweier Studierender:\n", "\n", "Es werden hier insgesamt zwei Strings und vier Integer Objekte auf zwei Instanzen des Typs \"`Studierende2`\" aufgeteilt.\n", "Der Aufruf ist hier ganz analog wie bei Funktionen, weil die Methode `__init__` selbst im Grunde eine Funktion ist." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ludwig = Studierende2(\"Ludwig Schmidt\", 9100541, 515)\n", "clara = Studierende2(\"Clara Bauer\", 97004131, 515)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Im Hintergrund passiert folgendes:\n", "Python erzeugt jeweils ein neues (leeres) Objekt im Speicher.\n", "Die Methode `__init__(...)` wird aufgerufen, wobei sie als erstes Argument dieses neue (leere) Objekt erhält.\n", "Die drei Zeilen innerhalb des Konstruktors setzen nun die Attribute des Objekts\n", "-- referenziert durch `self` --\n", "auf die übergebenen Werte.\n", "Dies passiert jeweils für beide Instanzierungsvorgänge und die entstehenden Objekte werden in `ludwig` und `clara` abgelegt." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zugriff auf Attribute:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'Ludwig Schmidt'" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ludwig.name" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "97004131" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "clara.mtrnr" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Typen der Studierenden:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "__main__.Studierende2" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(ludwig)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "__main__.Studierende2" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(clara)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "An dieser Stelle eine Zwischenfrage: Was ist der Typ einer Klasse?\n", "\n", "Beantworte diese Frage mit dem `type` Befehl und versuche dies zu erklären." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "type" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(Studierende2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Von hier aus können beliebige Operationen auf Basis beider Instanzen gemacht werden.\n", "\n", "Addition der Matrikelnummer von `ludwig` mit der Studienkennzahl von `clara`:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "9101056" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ludwig.mtrnr + clara.stkz" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zusammensetzen der Namen von Ludwig und Clara:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'Ludwig Schmidt & Clara Bauer'" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ludwig.name + \" & \" + clara.name" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Clara's Studienkennzahl multipliziert mit dem Integer 55:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "28325" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "clara.stkz * 55" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zur besseren Verdeutlichung, hier ein anderes Beispiel:\n", "\n", "Es wird eine Klasse **`Punkt`** definiert, welche einen Punkt in $\\mathbb{R}^2$ repräsentert." ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Punkt(object):\n", " def __init__(self, x, y):\n", " self.x = x\n", " self.y = y" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p1 = Punkt(4, -1)\n", "p1.x" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "-1" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p1.y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Methoden\n", "\n", "Wiederholt sich die Verarbeitung der Informationen in den Attributen,\n", "oder möchte man die Werte in den Attributen auf Ergebnisse von Verarbeitungen setzen,\n", "so wird diese in einer \"Methode\" (engl. \"method\") festgelegt.\n", "Das ist im Wesentlichen eine Funktion,\n", "welche als _erstes_ Argument die Instanz des Objekts (engl. \"Reciever\") erhält,\n", "und die restlichen Argumente je nach Definition der Methode.\n", "Dieses erste Argument wird üblicherweise immer `self` genannt.\n", "Dies ergibt eine direkte Assoziation von Funktionen zu Instanzen,\n", "und trägt damit zur Übersichtlichkeit bei.\n", "\n", "Der bereits vorgestellte Konstruktor `__init__` ist genau so eine Methode,\n", "wobei nur die Bezeichnung fest vorgegeben ist und automatisch beim Instanzieren aufgerufen wird.\n", "\n", "Im folgenden wird eine Klasse `Vorlesung` definiert,\n", "welche einen Namen, eine Lehrveranstaltungsnummer, einen Hörsaal, eine Menge von Studierenden, usw. enthält." ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Vorlesung(object):\n", " def __init__(self, name, lvnr):\n", " self.name = name\n", " self.lvnr = lvnr\n", " self.teilnehmer = set()\n", " \n", " def add_studierenden(self, s):\n", " self.teilnehmer.add(s)\n", " \n", " def anzahl_teilnehmer(self):\n", " return len(self.teilnehmer)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": false }, "outputs": [], "source": [ "pp1 = Vorlesung(\"Programmierpraktikum 1\", 25017)\n", "pp1.add_studierenden(clara)\n", "pp1.add_studierenden(ludwig)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pp1.anzahl_teilnehmer()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... und noch eine Studierende, die sich später angemeldet hat:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "paula = Studierende2(\"Paula Power\", \"1004131\", 515)\n", "pp1.add_studierenden(paula)\n", "pp1.anzahl_teilnehmer()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zur besseren Verdeutlichung, hier eine Erweiterung **`Punkt2`** von der Klasse `Punkt`.\n", "\n", "Die Methode `distanz_zum_ursprung` berechnet die Distanz zum Ursprung." ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Punkt2(object):\n", " \n", " def __init__(self, x, y):\n", " self.x = x\n", " self.y = y\n", " \n", " def distanz_zum_ursprung(self):\n", " from math import hypot\n", " return hypot(self.x, self.y) " ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "3.1622776601683795" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p2 = Punkt2(-1, 3)\n", "p2.distanz_zum_ursprung()" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "10.383159442096611" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p3 = Punkt2(5, 9.1)\n", "p3.distanz_zum_ursprung()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Stringdarstellung\n", "\n", "Neben der schon erwähnten eingebauten `__init__(...)` Methode,\n", "gibt es noch eine Reihe anderer interessanter Methoden.\n", "\n", "Zu den wichtigeren Methoden zählen `__str__()` bzw. `__repr__()`,\n", "welche aus den Attributen des Objekts eine lesbare Zeichenkette erzeugen.\n", "Dies macht Ausgaben für den Menschen viel informativer!" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "<__main__.Studierende2 at 0x7f2c92142748>" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# ohne __repr__, nur wenig Information:\n", "paula" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# wir fügen eine `__repr__` Methode hinzu und definieren eine neue Version der Klasse\n", "\n", "class Studierende3(object):\n", " def __init__(self, name, mtrnr, stkz):\n", " self.name = name\n", " self.mtrnr = mtrnr\n", " self.stkz = stkz\n", " def __repr__(self):\n", " return str(self)\n", " def __str__(self):\n", " return \"Student/in '%s' mit Mtrnr %s\" % (self.name, self.mtrnr)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Das Objekt muss neu instanziert werden und `paula`'s `__repr__` wird in der folgenden Codezelle aufgerufen,\n", "weil es in der letzten Zeile steht." ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Student/in 'Paula Power' mit Mtrnr 1004131" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "paula = Studierende3(\"Paula Power\", \"1004131\", 515)\n", "paula" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Nebenbemerkung:* `__repr__` ist allgmeiner aber `__str__` kommt nur bei `print` oder expliziter Zeichenkettenformatierung mittels `str()` zum Zug." ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Student/in 'Paula Power' mit Mtrnr 1004131\n" ] } ], "source": [ "print(paula)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Gibt es nur `__repr__` aber kein `__str__`, so wird `__repr__` als Fallback verwendet." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Operator Overloading\n", "\n", "Die `+`, `-`, `~`, `+=`, ... Operatoren können für jede Klasse speziell definiert werden.\n", "Beispielsweise lassen sich so mathematische Operationen für spezielle Objekte implementieren.\n", "Siehe [Liste dieser Methoden in Python2](https://docs.python.org/2/reference/datamodel.html#emulating-numeric-types).\n", "\n", "Als Beispiel wird `Vorlesung2` implementiert, welche das Hinzufügen eines Studierenden mittels `+=` Operator erlaubt.\n", "Hierfür wird die Methode `__iadd__` angegeben.\n", "Wichtig ist (um keine Verwirrungen durch `None`-Fehler auszulösen), dass das Objekt mittels `self` wieder zurückgegeben wird, um der auf der linken Seite stehenden Variablen zugewiesen werden zu können." ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Vorlesung2(object):\n", " def __init__(self, name, lvnr):\n", " self.name = name\n", " self.lvnr = lvnr\n", " self.teilnehmer = set()\n", " \n", " def __iadd__(self, s):\n", " self.teilnehmer.add(s)\n", " return self\n", " \n", " def anzahl_teilnehmer(self):\n", " return len(self.teilnehmer)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "collapsed": false }, "outputs": [], "source": [ "pp2 = Vorlesung2(\"Programmierpraktikum 2\", 25017)\n", "pp2 += clara\n", "pp2 += ludwig\n", "pp2 += paula" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Bei dem Abruf des Attributs `.teilnehmer` sieht man bereits,\n", "dass sich die Definition von `__repr__` bzw. `__str__` für `paula` in der Auflistung der Elemente als nützlich erweist." ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "{Student/in 'Paula Power' mit Mtrnr 1004131,\n", " <__main__.Studierende2 at 0x7f2c90250320>,\n", " <__main__.Studierende2 at 0x7f2c91e90ef0>}" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pp2.teilnehmer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ein weiteres Beispiel zur besseren Verdeutlichung:\n", "\n", "**Multiplikation zweier Zahlen modulo 13**,\n", "die beide als Instanz des Typs `Mod13` deklariert werden:" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Mod13(object):\n", " def __init__(self, n):\n", " self.n = n % 13\n", " \n", " def __repr__(self):\n", " return str(self.n)\n", " \n", " def __mul__(self, other):\n", " return Mod13(self.n * other.n)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "collapsed": true }, "outputs": [], "source": [ "z1 = Mod13(4)\n", "z2 = Mod13(9)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(4, 9)" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "z1, z2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\"handelsübliche\" Multiplikation von `4 * 9`, die allerdings über die `__mul__` Methode der Klasse `Mod13` abgewickelt wird.\n", "Daher ist das Ergebnis nicht 36!" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "z1 * z2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Der zurückgegebene Typ ist definitionsgemäß wieder `Mod13`:" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "__main__.Mod13" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(z1 * z2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dies schließt den ersten Teil zur Einführung in Klassen & Objekte ab.\n", "Der folgende Teil beschreibt, wie komplexere Strukturen aufgebaut werden können, wie Typen von Objekten erfragt werden und wie die Hintergründe dieser Techniken in Python sind." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Delegation\n", "\n", "Das \"[Delegation Pattern](http://en.wikipedia.org/wiki/Delegation_pattern)\"\n", "ist eine sehr häufig verwendete Methode, wie modularisierte Klassen aufgebaut werden.\n", "Im Konstruktor einer Klasse wird eine andere Klasse instanziert,\n", "als Attribut gespeichert und später dessen Attribute/Methoden verwendet.\n", "Dies erlaubt, problemlos größere und mächtigere Strukturen aufzubauen,\n", "während gleichzeitig -- Dank dieser Modularisierung -- die Komplexität in Schach gehalten wird.\n", "\n", "Es folgt ein einfaches, abstraktes Beispiel, in dem eine Klasse `Pipe` eine Klasse `Circle` verwendet.\n", "Wichtig ist zu erkennen, dass\n", "\n", "1. die Instanzierung von `Circle` in das Attribut `circle`\n", " bei der Klasseninstanzierung der `Pipe`-Klasse in die Variable `pipe` passiert,\n", "1. in der Methode `volume`, die Methode `area` des Attributes `circle` aufgerufen wird." ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Circle(object):\n", " \n", " def __init__(self, r):\n", " self.r = r\n", " \n", " def area(self):\n", " from math import pi\n", " return 2 * self.r**2 * pi\n", "\n", "class Pipe(object):\n", " \n", " def __init__(self, diameter, height):\n", " self.circle = Circle(diameter / 2.)\n", " self.height = height\n", " \n", " def volume(self):\n", " return self.circle.area() * self.height" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "332.38050274980003" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pipe = Pipe(4.6, 10.0)\n", "pipe.volume()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## \"Ableitung\" von Klassen (engl. 'extension')\n", "\n", "Das Ableiten einer Klasse hat nichts mit dem mathematischen Ableiten zu tun.\n", "Es handelt sich hier um eine Technik, um schon bestehende Definitionen zu erweitern.\n", "Dies ist fundamental anders, als die schon vorgestellte \"Delegation\".\n", "\n", "Beispielsweise möchten wir eine `Vorlesung2` Klasse definieren,\n", "die alles von `Vorlesung` erbt und noch eine weitere Methode hinzufügt.\n", "\n", "Erzielt wird dies durch Angeben der *Elternklasse* in der Definitionszeile der Klasse:" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Vorlesung3(Vorlesung):\n", " def remove_studierenden(self, s):\n", " self.teilnehmer.remove(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instanzierung und Methoden wie `add_studierenden` funktionieren wie in `Vorlesung`:" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ana2 = Vorlesung3(\"Analysis 2\", 25007)\n", "ana2.add_studierenden(paula)\n", "ana2.anzahl_teilnehmer()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Neu ist hingegen, dass nun auch `remove_studierenden` existiert:" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ana2.remove_studierenden(paula)\n", "ana2.anzahl_teilnehmer()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Allgemeiner: Die abgeleitete Kind-Klasse erbt alle Methoden und Attribute der Eltern-Klasse.\n", "Dabei können Kind-Klassen die Methoden überschreiben. Das heißt, sie definieren eine gleichlautende Methode,\n", "welche effektiv die Methode der Eltern-Klasse ersetzt.\n", "\n", "Hierbei ist es weiterhin möglich, auf die Eltern-Methode zuzugreifen.\n", "Hierfür gibt es das Schlüsselwort `super`: `super(Kind, self).methode(...)`.\n", "Besonders häufig wird dies in der `__init__`-Methode verwendet, um nach einer Initialisierung der Attribute der Kind-Klasse auch die Eltern-Klasse zu initialisieren.\n", "\n", "Es folgt nun ein abstrakteres Kind/Eltern Klassen Beispiel, welches all dies verdeutlicht." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Eltern(object):\n", " \n", " def __init__(self, x):\n", " self.x = x\n", " print(\"Eltern __init__ fertig. x = %d\" % self.x)\n", " \n", " def double(self):\n", " print(\"2 * x = %d\" % (2*self.x))\n", "\n", "class Kind(Eltern):\n", " \n", " def __init__(self, x, y):\n", " super(Kind, self).__init__(x)\n", " self.y = y\n", " print(\"Kind __init__ fertig. y = %d\" % self.y)\n", " \n", " def double(self):\n", " print(\"2*x + 2*y = %d\" % (2*self.x + 2*self.y))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Die Eltern-Klasse mit der `double()` Methode:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Eltern __init__ fertig. x = 21\n", "2 * x = 42\n" ] } ], "source": [ "e = Eltern(21)\n", "e.double()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wir testen nun die Kind-Klasse mit der überschriebenen `double()` Methode und der Initialisierung der Eltern-Klasse im `__init__` Konstruktor.\n", "Man sieht, bei der Ausführung des Konstruktors wird zuerst der Konstruktor der Elternklasse abgearbeitet,\n", "und anschließend der der Kindklasse." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Eltern __init__ fertig. x = 11\n", "Kind __init__ fertig. y = 12\n", "2*x + 2*y = 46\n" ] } ], "source": [ "k = Kind(11, 12)\n", "k.double()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Fazit\n", "\n", "Prinzipiell gibt es daher drei Möglichkeiten, wie Klassen und Klassen-Ableitungen zur Modularisierung verwendet werden können:\n", "\n", "* Gar nicht: Delegation ist die üblicherweise verwendete Art, wie Funktionalität aus modularisierten Bausteinen zusammengestellt wird.\n", "* Ableitung ohne Überschreiben: Wenn eine klare hierarchische Struktur besteht, macht es Sinn, einen gemeinsamen Satz von Funktionalitäten in einer Elternklasse zu sammeln. Dies impliziert insbesondere, dass es mehrere unterschiedliche Kind-Klassen geben muss.\n", "* Ableitung mit Überschreiben: Dies ändert das verhalten der Elternklasse in den jeweils überschriebenen Methoden. Es kommt auf die generelle Struktur des Projekts und die \"sozialen Strukturen\" an, ob dies Sinn macht." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Instanztest\n", "\n", "Oft muss anhand des Types eines Objektes entschieden werden, was zu tun ist oder ob überhaupt ein Objekt mit dem richtigen Typ übergeben wurde.\n", "Das wird mit der eingebauten Funktion `isinstance` getestet:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Basistypen:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(9, int)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`k` ist vom Typ `Kind`, jedoch aufgrund der Ableitung der Klasse indirekt auch eine Instanz der Klasse `Eltern`:" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(k, Eltern)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`e` ist Instanz der Eltern-Klasse, und daher keine Instanz der abgeleiteten Kind-Klasse:" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(e, Kind)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... und genausowenig ein Basistyp:" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(e, float)" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(u\"Uµı¢ð€-string\", str)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Bonus: Behind-the-scenes\n", "\n", "Intern sind die Attribute einer Klasse durch das bereits vorgestellte Dictionary implementiert.\n", "Einzig die Punktnotation ist neu, und nur bei Klassen verfügbar.\n", "Sie wird über die Methode \"[__getattr__](https://docs.python.org/2/reference/datamodel.html#object.__getattr__)\" \n", "der Basisklasse `object` bereitgestellt.\n", "Für spezielle Aufgaben kann sie beliebig erweitert werden.\n", "\n", "Hier nun der Inhalt des Dictionaries der Kindes `k`:" ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "{'x': 11, 'y': 12}" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "k.__dict__" ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "dict" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(k.__dict__)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Änderungen an dem Dictionary dieser Kind-Instanz ..." ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "collapsed": true }, "outputs": [], "source": [ "k.__dict__[\"z\"] = 99" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... haben sofort eine Auswirkung. Hier nun der Zugriff auf das neu erstellte Attribut `z` des Objekt's `k`:" ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "99" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "k.z" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wo ist die Methode `double()`? Wie schon erwähnt, Methoden werden in der Klasse definiert.\n", "Klassen haben ebenfalls dieses `__dict__`-Dictionary, allerdings hier eben ohne die objektspezifischen Attribute `x` und `y`:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "mappingproxy({'__doc__': None,\n", " '__init__': ,\n", " '__module__': '__main__',\n", " 'double': })" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Kind.__dict__" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Um weitere strukturelle Informationen einer Klasse oder Objektinstanz abzurufen gibt es \"Reflection\".\n", "Dies wird in einem späteren Kapitel genauer erklärt.\n", "Für Ungeduldige: [inspect py2](https://docs.python.org/2/library/inspect.html)\n", "bzw. [inspect py3](https://docs.python.org/3/library/inspect.html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Bonus: Metaprogramming\n", "\n", "Etwas verwirrend, aber durchaus sinnvoll, ist die Tatsache, dass Klassen selbst Objekte sind.\n", "Sie sind Instanzen des Typs `type` und können auch in einem Programm erzeugt werden bzw.\n", "lässt sich das interne Verhalten einer Klasse dadurch beeinflussen." ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "type" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(Studierende3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Abseits der hier vorgestellten Art mittels `class ...:` eine Klasse zu definieren,\n", "können Klassen auch rein programmatisch definiert werden.\n", "\n", "Hier wird nun eine Klasse `ProgCls` mittels der `type()` Funktion erzeugt.\n", "Sie hat eine `__init__`-Methode, welche das Attribut `.x` setzt und eine Methode `f()` mit einer kleinen Berechnung.\n", "\n", "Die damit erzeugte Klasse, in der Variablen `ProgCls`, kann wie gewohnt zu Objekten (hier `pc1` und `pc2`) instanziert werden." ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "collapsed": true }, "outputs": [], "source": [ "ProgCls = type('ProgCls', (), {\n", " 'f' : lambda self : 3 * self.x,\n", " '__init__': lambda self, x : setattr(self, 'x', x)\n", " })" ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "63" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pc1 = ProgCls(21)\n", "pc1.f()" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "24" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pc2 = ProgCls(8)\n", "pc2.f()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Links\n", "\n", "Wer mehr zu den einzelnen Themen lernen möchte:\n", "\n", "* [\"Classes\" in Object-Oriented Programming in Python](http://python-textbok.readthedocs.org/en/latest/Classes.html)\n", "* Lange StackOverflow Antwort über [Klassen sind Objekte](http://stackoverflow.com/a/6581949/54236) in Python" ] } ], "metadata": { "kernelspec": { "display_name": "Anaconda (Python 3)", "language": "python", "name": "anaconda3" }, "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.5.2" }, "name": "2-6-datentypen_und_objekte.ipynb" }, "nbformat": 4, "nbformat_minor": 0 }