diff --git a/Uebung03/OOP-Grundlagen.ipynb b/Uebung03/OOP-Grundlagen.ipynb
index 401822151082ca17451751d514b7cfe5207d3bd8..4debfd3482854a59dd9e0ff7fe9489d86831e3eb 100644
--- a/Uebung03/OOP-Grundlagen.ipynb
+++ b/Uebung03/OOP-Grundlagen.ipynb
@@ -350,13 +350,13 @@
     "|  | __repr__| gibt die Zeichenkette zurück, die durch print ausgegeben wird|\n",
     "|  |__init__ | wird automatisch beim erzeugen eines Objekts aufgerufen |\n",
     "|  |__del__ | wird automatisch aufgerufen, wenn das Objekt gelöscht wird |\n",
-    "| [] | __getattr__(self,index),__setattr__(self,index,val) | wird durch den Indexoperator aufgerufen|\n",
+    "| [] | __getitem__(self,index),__setitem__(self,index,val) | wird durch den Indexoperator aufgerufen|\n",
     "| | __len__| wird aufgerufen, um die Länge des Objektes mit len zu ermitteln|\n",
     "\n",
     "\n",
     "Da die Methoden Objektmethoden sind muss ihr erster Parameter das angesprochene ```self``` sein. Weitere Parameter kommen je nach Operator hinzu. Alle Rechenoperatoren sind z.B. binäre Operatoren. Die magic methods haben einen zweiten Parameter, der das Objekt auf der rechten Seite des + enthält\n",
     "\n",
-    "Die verfügbaren Magic methods sind fest bestimmt (siehe Liste ganz unten auf https://www.tutorialsteacher.com/python/magic-methods-in-python), und beginnen und enden immer mit zwei Unterstrichen ```__```. Die bereits bekannte Methode ```__init__()``` gehört demnach auch zu diesen Methoden. Im Fallbeispiel oben ist dies beispielhaft für die Verwendung in ```print( )``` gezeigt. Das ist die Methode ```__repr__(self)```.\n",
+    "Die verfügbaren Magic methods sind fest bestimmt (siehe Liste ganz unten auf https://rszalski.github.io/magicmethods/#operators), und beginnen und enden immer mit zwei Unterstrichen ```__```. Die bereits bekannte Methode ```__init__()``` gehört demnach auch zu diesen Methoden. Im Fallbeispiel oben ist dies beispielhaft für die Verwendung in ```print( )``` gezeigt. Das ist die Methode ```__repr__(self)```.\n",
     "\n"
    ]
   },
diff --git a/Uebung03/Uebung03_LSG.ipynb b/Uebung03/Uebung03_LSG.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..1f1940bb48bf1735df6bad6a802d6269a2cd8724
--- /dev/null
+++ b/Uebung03/Uebung03_LSG.ipynb
@@ -0,0 +1,365 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "757b9a6c-6599-4cd6-93c5-c4fb30ed5b53",
+   "metadata": {},
+   "source": [
+    "# <font color='blue'>**Ãœbung 3 - Objektorientierte Programmierung**</font>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d3a49027-9186-48a6-ba9f-332428e93bd9",
+   "metadata": {},
+   "source": [
+    "## <font color='blue'>**Die Grundlagen von Objektorientierter Programmierung**</font>\n",
+    "\n",
+    "In den vorherigen Übungen wurden die grundlegenden Elemente zur Ablaufsteuerung von Programmen behandelt. In dieser Übung führen wir ein wichtiges Konzept der Programmierung ein, die Objektorientierte Programmierung. Diese hilft sowohl bei der Strukturierung komplexer Projekte, der Verständlichkeit von Quellcode und dem gemeinsamen Arbeiten. Die Grundidee dahinter ist es, zusammengehörende Daten zusammen mit dazu passenden Operationen in (selbst) definierten Datentypen zusammenzufassen.    \n",
+    "\n",
+    "### **Weitere Notebooks, die dir helfen könnten**\n",
+    "* Python Grundlagen Teil 1\n",
+    "* Python Grundlagen Teil 2\n",
+    "* OOP Grundlagen\n",
+    "\n",
+    "### **Vorkenntnisse**\n",
+    "* Ãœbung 1\n",
+    "* Ãœbung 2\n",
+    "\n",
+    "### **Lernziele**\n",
+    "* Klassen und Objekte\n",
+    "* Attribute und Methoden\n",
+    "* Spezielle Methoden"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b0e426c9-9a66-4bc6-a0be-fc8e08c18858",
+   "metadata": {},
+   "source": [
+    "# <font color='blue'>**Abschnitt 1 - Einfache Klasse**</font>\n",
+    "## <font color='blue'>*Aufgabe*</font>\n",
+    "\n",
+    "Für den ersten Kontakt mit Klassen und Objekten wollen wir mit einem einfachen Alltagsbeispiel beginnen.\n",
+    "\n",
+    "Erstelle einen Datentyp, mit dem ein Supermarkt seine Artikel verwalten kann. Die Artikel sollen einen Namen, eine Menge und einen Preis pro Mengeneinheit haben. Dazu soll die Datenstruktur Methoden besitzen, mit denen der Supermarkt den Lagerbestand auffüllen kann, sowie eine bestimmte Menge eines Artikels (soweit vorhanden) verkaufen kann. Beim Verkaufen soll eine Ausgabe über die verkaufte Menge, den Preis und die verbleibende Menge gegeben werden. Außerdem soll ermittelbar sein, wieviele verschiedene Artikel es gibt.\n",
+    "\n",
+    "Teste das Programm anhand eines Supermarktes der zu Beginn 500 kg Zucker zu 1 Euro pro kg, und 1000 kg Mehl zu 1,50 Euro pro kg auf Lager hat. Er füllt das Lager um 600 kg Zucker auf, und ein Kunde kauft 4 kg Mehl. Nutze die erstellte Klasse, um die neuen Lagerbestände zu ermitteln, sowie den vom Kunden gezahlten Preis.  "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b29c819a-aedd-4232-bcd7-eedcd810695c",
+   "metadata": {},
+   "source": [
+    "## <font color='blue'>*Lösung*</font>\n",
+    "\n",
+    "Klassendefinition und Instanziierung:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "6b0c8c9d-f3b6-47fc-9de7-04f557ed5511",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "class Artikel:#definiert eine Klasse namens artikel\n",
+    "    \n",
+    "    anz_artikel = 0 #Eine Klassenvariable, die mitzählt, wie viele verschiedene Objekte der Klasse Artikel existieren\n",
+    "    \n",
+    "    def __init__(self, name, menge, preis):#Der Konstruktor der Klasse. Wird immer aufgerufen, wenn ein Objekt der Klasse erzeugt wird\n",
+    "        self.name = name#legt eine Objektvariable an, die den Namen enthalten soll, der dem Konstruktor übergeben wurde\n",
+    "        if menge > 0:#überprüft, ob die angegebene Startmenge größer 0 ist\n",
+    "            self.menge = menge#wenn ja wird eine Objektvariable angelegt, die die Startmenge enthält\n",
+    "        else:\n",
+    "            self.menge = 0#Falls die angegebene Menge nicht größer 0 war, wird die Startmenge auf 0 gesetzt. Es soll keine negative menge möglich sein\n",
+    "        self.preis = preis#Der Preis des Artikels wird auch in objektvariablen gespeichert\n",
+    "        Artikel.anz_artikel += 1#Die Anzahl der existierenden Artikel um eins erhöhen\n",
+    "        \n",
+    "    def auffuellen(self, menge):#Eine Objektmethode, die die menge der Waren auffüllt\n",
+    "        self.menge += menge#addiert die aufzufüllende Menge auf die bestehende Menge des Artikels\n",
+    "        \n",
+    "    def verkaufen(self, verkaufsmenge):#Eine Objektmethode zum Verkaufen\n",
+    "        if verkaufsmenge > self.menge:#Falls mehr verkauft werden soll als überhaupt vorhanden ist\n",
+    "            verkaufsmenge = self.menge#die Verkaufsmenge auf die gerade verfügbare Menge reduzieren\n",
+    "        self.menge -= verkaufsmenge#die Verkaufsmenge von der Vorratsmenge des Artikels abziehen\n",
+    "        print(f\"{verkaufsmenge} kg {self.name} fuer {self.preis*verkaufsmenge} Euro verkauft. Es verbleiben {self.menge} kg auf Lager.\")#Ausgabe was verkauft wurde und wie viel es gekostet hat. Diese Schreibweise für Zeichenketten ist sehr praktisch, wenn man viele Zahlen einbinden möchte\n",
+    "    def __del__(self):#der Destruktor. Wird aufgerufen, wenn ein Objekt gelöscht wird\n",
+    "        Artikel.anz_artikel-=1#Die Gesamtzahl der vorhandenen Artikel muss um eins heruntergezählt werden, da ein Artikel gerade gelöscht wird\n",
+    "zucker = Artikel(\"Zucker\", 500., 1.)#Erzeugt zucker. Ein Objekt der Klasse Artikel. Die Parameter sind nötig, da __init__ von Artikel 3 Parameter benötigt (+ das self, das bei Objektmethodenaufrufen implizit der Parameterliste hinzugefügt wird)\n",
+    "mehl = Artikel(\"Mehl\", 1000., 1.5)\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d046ee6a",
+   "metadata": {},
+   "source": [
+    "Auffüll- und Verkaufsoperationen, sowie Testausgaben (beliebig oft wiederholbar):"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "7b68c5f0",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "4 kg Mehl fuer 6.0 Euro verkauft. Es verbleiben 996.0 kg auf Lager.\n",
+      "Bestand Zucker: 1100.0 kg\n",
+      "Bestand Mehl: 996.0 kg\n"
+     ]
+    }
+   ],
+   "source": [
+    "zucker.auffuellen(600)#Ruft eine Objektmethode auf. Der Parameter self der Funktion auffuellen wird hierbei mit dem Objekt zucker initialisiert, da zucker vor dem Punkt steht\n",
+    "mehl.verkaufen(4)#Das gleiche fügr Mehl mit der verkaufen-Methode\n",
+    "\n",
+    "print(f\"Bestand Zucker: {zucker.menge} kg\")#Auf Objektvariablen kann mit dem punkt zugegriffen werden. zucker.menge ist die Variable menge des Objetkes zucker\n",
+    "print(f\"Bestand Mehl: {mehl.menge} kg\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "id": "59fd42a8-6344-40a7-af32-5be4e24f2fa1",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Anzahl der definierten Artikel 2\n",
+      "Anzahl der definierten Artikel 2\n",
+      "Anzahl der definierten Artikel 2\n",
+      "Anzahl der definierten Artikel 1\n"
+     ]
+    }
+   ],
+   "source": [
+    "#Ein kurzes Beispiel um dir noch einmal die Funktion von Python Variablen zu verdeutlichen\n",
+    "print(\"Anzahl der definierten Artikel\",Artikel.anz_artikel)# Es gibt 2 Artikel, mehl und zucker. \n",
+    "mehl2=mehl#Erzeugt keinen neuen Artikel mehl2 und mehl verweisen auf das selbe Objekt der Klasse Artikel\n",
+    "print(\"Anzahl der definierten Artikel\",Artikel.anz_artikel)# Es gibt immer noch 2 Artikel. mehl2 und mehl sind der identische Artikel\n",
+    "mehl=2#Belegt die Variable mehl mit einem neun Wert bzw. Objekt. Einer 2\n",
+    "print(\"Anzahl der definierten Artikel\",Artikel.anz_artikel)# Es gibt immer noch 2 Artikel. mehl2 und zucker. Da das \"Mehl\"-Objekt mit mehl2 einen zweiten Namen hat wurde es in der letzten Zeile nicht gelöscht\n",
+    "mehl2=4#Die Variable mehl2 wird jetzt auch neu belegt. Für das \"Mehl\"-Objekt gibt es jetzt keinen Variablennamen mehr. Es wird dadurch gelöscht\n",
+    "print(\"Anzahl der definierten Artikel\",Artikel.anz_artikel)# Es gibt nur noch einen Artikel. Den Zucker"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c665968c",
+   "metadata": {},
+   "source": [
+    "## <font color='blue'>*Hinweise*</font>\n",
+    "\n",
+    "Es soll eine Klasse und ein Hauptprogramm erstellt werden. In dem Hauptprogramm sollen Objekte der Klasse verwendet werden, um die beispielhafte Testaufgabe zu erfüllen. Natürlich kannst du dich hier auch frei austoben und mehrere Verkäufe machen, oder testen, ob die Klasse beim Verkaufen korrekt erkennt, wenn nicht genug auf Lager ist. Es darf keine negativen Mengen geben.\n",
+    "\n",
+    "Wir machen folgende Vorschläge für die Namen der Klasse, Attribute und Methoden:\n",
+    "Klasse ```Artikel```,\n",
+    "Attribute ```anz_artikel```, ```menge```, ```preis```, ```name```\n",
+    "Methoden ```__init__()```, ```auffuellen()```, ```verkaufen()```\n",
+    "\n",
+    "Für die Syntax benötigst du Informationen aus dem Grunlagen OOP Notebook. Für diesen Abschnitt benötigst du Klassen und Objekte, Klassen- und Objektattribute, sowie Objektmethoden.\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "46d21f3a-b37e-49d3-864f-587b09ba0b89",
+   "metadata": {},
+   "source": [
+    "# <font color='blue'>**Abschnitt 2 - Vektorrechnungen**</font>\n",
+    "\n",
+    "Nachdem du nun erste Erfahrungen mit der Objektorientierten Programmierung gemacht hast, wollen wir uns einem mathematischeren Thema widmen. Dabei wollen wir auch das Thema spezielle Methoden (auch magic method genannt) einführen. Diese erlauben es uns in dieser Aufgabe, Vektorrechnung mit den bekannten Operatoren wie mit \"normalen\" Variablen auch, durchzuführen."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6e25b06f",
+   "metadata": {},
+   "source": [
+    "## <font color='blue'>*Aufgabe*</font>\n",
+    "\n",
+    "Berechne den Betrag des Vektors  c = a-b, mit den Vektoren a = (1,2,3) und b = (5,-4,3). Außerdem soll das Skalarprodukt a * b berechnet werden. Erstelle dazu eine Klasse ```Vektor3``` für die dreidimensionalen Vektoren so, dass die bereits gegebenen Codezeilen erfolgreich ausgeführt werden können."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e7333454-be6d-487d-a39e-ba509472aa30",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "## <font color='blue'>*Lösung*</font>"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "id": "dc85e62c-a188-46ab-b7c5-55fc44d8bbbf",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Betrag des Vektors c: 7.211102550927978\n",
+      "Skalarprodukt a * b: 6\n"
+     ]
+    }
+   ],
+   "source": [
+    "class Vektor3:#Eine Klasse Vektor\n",
+    "    def __init__(self, coords):\n",
+    "        \n",
+    "        if len(coords)!= 3:#Überprüft, ob der parameter coords 3 Werte enthält. Das kann eine Liste oder ein Tupel sein\n",
+    "            raise ValueError('Invalid input.')#Falls nicht kann so ein Fehler ausgelöst werden. Ihr könnt es selbst ausprobieren, indem ihr versucht einen Vektor3 aus einer Liste mit mehr als 3 Einträgen zu erzeugen\n",
+    "            \n",
+    "        self.x = coords[0]#Setzt für x,y und z Objektvariablen\n",
+    "        self.y = coords[1]\n",
+    "        self.z = coords[2]\n",
+    "        \n",
+    "    def get_betrag(self):#Eine Methode, um die Länge des Vektors zu berechnen\n",
+    "        return (self.x**2 + self.y**2 + self.z**2)**0.5\n",
+    "    \n",
+    "    def __mul__(self, vec2):#Eine magic method, die Aufgerufen wird, wenn ein Vec3 auf der linken Seite des * zeichens steht\n",
+    "        res = (self.x * vec2.x) + (self.y * vec2.y) + (self.z * vec2.z)\n",
+    "        return res\n",
+    "    \n",
+    "    def __sub__(self, vec2):#das gleiche für +\n",
+    "        coords_c = [self.x - vec2.x, self.y - vec2.y, self.z - vec2.z]\n",
+    "        c = Vektor3(coords_c)\n",
+    "        return c\n",
+    "    #Ein Paar kleine Extras, nur zum Zeigen\n",
+    "    def __len__(self):#Falls len() mit einem Vector3-Objekt aufgerufen wird, kommt das Ergebnis 3 heraus. Der Vec3 ist 3 Elemente lang\n",
+    "        return 3\n",
+    "    def __getitem__(self,index):#durch diese Funktion kann der Indexoperator verwendet werden, um Werte aus dem Vektor zu bekommen. Nicht ganz perfekt da jeder index größer 2 das z zurückliefert\n",
+    "        if index==0:\n",
+    "            return self.x\n",
+    "        elif index==1:\n",
+    "            return self.y\n",
+    "        else:\n",
+    "            return self.z\n",
+    "    def __setitem__(self,index,value):#durch diese Methode kann man auch Elementen des Vektors mit dem Indexoperator Werte zuweisen\n",
+    "        if index==0:\n",
+    "            self.x=value\n",
+    "        elif index==1:\n",
+    "            self.y=value\n",
+    "        else:\n",
+    "            self.z=value\n",
+    "a = Vektor3([1,2,3])\n",
+    "b = Vektor3([5,-4,3])\n",
+    "\n",
+    "c = a-b\n",
+    "\n",
+    "print(\"Betrag des Vektors c: \" + str(c.get_betrag()))\n",
+    "print(\"Skalarprodukt a * b: \" + str(a*b))\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "id": "a2987eb5-b92f-47fd-b32e-eefeb02d09f7",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "1\n",
+      "4\n"
+     ]
+    }
+   ],
+   "source": [
+    "#Ein paar kleine Extras\n",
+    "print(a[0])#Durch das __getitem__ funktioniert der Zugriff auf die einzelnen Komponenten des Vektors\n",
+    "a[0]=4#In diesem Fall wird a.__setitem__(4) aufgerufen\n",
+    "print (a[0])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9dfb2bc6-c09d-4109-a999-db7f73aee7c9",
+   "metadata": {},
+   "source": [
+    "## <font color='blue'>*Weitere Operatoren*</font>\n",
+    "Eine vollständige Liste sämtlicher magic-functions von Python Objekten findet ihr https://rszalski.github.io/magicmethods/#operators\n",
+    "Auch das in der Vorlesung angesprochene Problem, dass in Falle des Vektor3 ein Aufruf eines Operators nur funktioniert, wenn das Vector3-Objekt auf der linken Seite steht, also\n",
+    "```\n",
+    "a=Vector3([1,2,3])\n",
+    "b= a+4 #wird zu a.__add__(4)\n",
+    "b= 4+a #Funktioniert nicht, da die 4 keine __add__ Methode hat, bzw. diese nicht für Objekte der Klasse Vector3 gedacht ist\n",
+    "```\n",
+    "kann über magic-functions mit Einschränkungen gelöst werden. Die entsprechende Funktion würde ```__radd__``` heißen. Näheres findet ihr unter dem Link"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ac9ba52d",
+   "metadata": {},
+   "source": [
+    "## <font color='blue'>*Hinweise*</font>\n",
+    "\n",
+    "Nun benötigst du noch den Abschnitt über spezielle Methoden aus dem OOP-Grundlagen Notebook. Diese erlauben es, die Methoden so zu definieren, dass die Operationen wie a-b mit den selbst definierten Vektoren funktionieren.\n",
+    "\n",
+    "Es ist dir selbst überlassen, ob du die Einträge der Vektoren als eine Liste, oder als drei einzelne Werte speicherst.\n",
+    "\n",
+    "Das Ergebnis einer Summe oder Differenz ist ein neuer Vektor.\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "595a83db-5a61-4048-84d3-e5f1ca14c263",
+   "metadata": {},
+   "source": [
+    "# <font color='blue'>**Aufgabe zum selbst probieren**</font>\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ca559a03-b79b-4f1a-a128-4e3e3e177017",
+   "metadata": {},
+   "source": [
+    "Erweitere die Vektorklasse um weitere Operationen, die dir sinnvoll erscheinen. Ebenso könnte es interessant sein, die Beschränkung auf 3 Dimensionen aufzuheben. Beachte dabei, dass die Operationen bestimmte Anforderungen haben. So funktioniert das Kreuzprodukt nur mit Vektoren mit drei Dimensionen, Addition und Substraktion nur für Vektoren gleicher Dimension etc. Die Funktionen müssen das dann überprüfen und ansonsten eine Warnung ausgeben, dass die Operation keine sinnvolle Lösung hat.\n",
+    "\n",
+    "\n",
+    "## <font color='blue'>**Lösung**</font>\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "6a4e935d-1468-41be-a175-71505b044a38",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "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.8.3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}