.. include:: markup.rst ************************************ Ingenieurwissenschaftliches Arbeiten ************************************ In den Ingenieurswissenschaften kommt dem :mark:`Scientific Computing` (d.h. der Numerik) ein immer größerer Stellenwert zu (siehe Abbildung `Scientific Computing in Python`_). .. _`Scientific Computing in Python`: .. figure:: images/python_scientific.png :scale: 40 % :align: center Scientific Computing in Python Python und IPython haben wir bereits kennen gelernt. In diesem Kapitel werden wir die Bibliotheken: * NumPy * Scipy * Matplotlib * Jupyter genauer kennen lernen. Einige kennen wahrscheinlich `Matlab `_. Python in Kombination mit NumPy, SciPy, Matplotlib und Pandas kann als :mark:`vollwertiger Ersatz für MATLAB` genutzt werden. .. figure:: images/python_matlab.png :scale: 40 % :align: center NumPy ===== `NumPy `_ ist ein Akronym für :mark:`"Numerisches Python"`. Das Modul wird importiert mittels : :: import numpy Wesentlich häufiger findet man jedoch die Abkürzung: :: import numpy as np Erzeugung von Arrays -------------------- `NumPy`_ Arrays können einfach aus Python Listen erzeugt werden: :: In [1]: import numpy as np # Import des Moduls In [2]: a = np.array([2, 4, 5, 9]) # Erzeuge NumPy Array aus Liste In [3]: a Out[3]: array([2, 4, 5, 9]) # 'array' zeigt den Objecttyp an In [4]: type(a) # Objecttyp Abfrage Out[4]: numpy.ndarray In [5]: a.dtype # Datentyp Abfrage Out[5]: dtype('int32') Alternativ kann man `NumPy`_ Funktionen zur Erzeugung verwenden: :: In [1]: b = np.zeros((2, 3)) # Erzeuge 2D Array mit "0" In [2]: b Out[2]: array([[0., 0., 0.], [0., 0., 0.]]) In [3]: c = np.ones((2,3,4), np.int) # 3D Array mit "1", Datentyp Integer In [4]: c Out[4]: array([[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]], [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]]) `NumPy`_ bietet Funktionen, um Arrays aus Intervallen zu erzeugen: :: In [1]: import numpy as np In [2]: a = np.arange(1,7) # Array mittels Intervall: start, stop In [3]: a # Objecttyp: numpy.ndarray Out[3]: array([1, 2, 3, 4, 5, 6]) In [4]: c = np.arange(4, dtype=np.float) # 1D array mit Datentyp Angabe In [5]: c Out[5]: array([0., 1., 2., 3.]) Die ``arange`` Funktion sollte nicht mit der Standard Python ``range`` Funktion verwechselt werden: :: In [1]: x = range(1,7) # Standard Python range Funktion In [2]: x Out[2]: range(1, 7) # .. ist ein Iterator In [3]: list(x) # Umwandlung in Liste Out[3]: [1, 2, 3, 4, 5, 6] Grundlegende Rechenoperationen ------------------------------ Einige Grundlegen Rechenoperationen mit NumPy-Arrays lauten: :: In [1]: import numpy as np In [2]: a = np.array( [20,30,40,50] ) In [3]: b = np.arange( 4 ) In [4]: b Out[4]: array([0, 1, 2, 3]) In [5]: c = a-b In [6]: c Out[6]: array([20, 29, 38, 47]) In [7]: b**2 Out[7]: array([0, 1, 4, 9], dtype=int32) In [8]: 10*np.sin(a) Out[8]: array([ 9.12945251, -9.88031624, 7.4511316 , -2.62374854]) In [9]: a<35 Out[9]: array([ True, True, False, False]) Anders als in vielen Matrixsprachen arbeitet der Produktoperator ``*`` in NumPy-Arrays Elementweise. Das Matrixprodukt kann mit dem Operator ``@`` (in Python> = 3.5) oder der Funktion ``dot`` ausgeführt werden: :: In [10]: A = np.array( [[1,1], ...: [0,1]] ) In [11]: B = np.array( [[2,0], ...: [3,4]] ) In [12]: A * B # Elementweises Produkt Out[12]: array([[2, 0], [0, 4]]) In [13]: A @ B # Matrix Produkt seit Python 3.5 Out[13]: array([[5, 4], [3, 4]]) In [14]: A.dot(B) # Altes Matrix "dot" Produkt Out[14]: array([[5, 4], [3, 4]]) Einfache NumPy-Array Funktionen ------------------------------- Einfache NumPy-Array Funktion sind: :: In [15]: rg = np.random.default_rng() # erzeuge einen Zufallszahlen Generator In [16]: a = rg.random((2,3)) In [17]: a Out[17]: array([[0.1798896 , 0.11681055, 0.93822687], [0.2420247 , 0.53870043, 0.34058512]]) In [18]: a.sum() # Summe aller Array Elemente Out[18]: 2.356237274510712 In [19]: a.min() # Minimum Out[19]: 0.11681055455275102 In [20]: a.max() # Maximum Out[20]: 0.9382268744684458 In [21]: np.max(a) # Alternative Schreibweise Out[21]: 0.9382268744684458 In [22]: np.sqrt(a) Out[22]: array([[0.42413394, 0.34177559, 0.96862112], [0.49196005, 0.73396214, 0.58359671]]) In [23]: np.exp(a) Out[23]: array([[1.1970852 , 1.12390649, 2.55544627], [1.27382565, 1.71377823, 1.4057699 ]]) Array Manipulation ------------------ Typische NumPy-Array Manipulationen sind: :: In [1]: import numpy as np In [2]: a = np.array( [[1.1 , 1.9, 2.5], # beliebiger Array ...: [3.2, 6.7, 1.4 ]]) In [3]: a.shape # 2D Array: 2 Zeilen, 3 Spalten Out[3]: (2, 3) In [4]: b = np.floor(a) # (ab)runden In [5]: b Out[5]: array([[1., 1., 2.], [3., 6., 1.]]) In [6]: b.ravel() # 1D Array "flatten" Out[6]: array([1., 1., 2., 3., 6., 1.]) In [7]: b.reshape(3,2) # Neue "Shape": 3 Zeilen, 2 Spalten Out[7]: array([[1., 1.], [2., 3.], [6., 1.]]) In [8]: b.T # Transponierte von b Out[8]: array([[1., 3.], [1., 6.], [2., 1.]]) Zeiger und Kopien ----------------- NumPy-Arrays verhalten sich gleich wie veränderbare Datentypen z.B. Listen: :: In [1]: import numpy as np In [2]: a = np.array( [2, 4, 7] ) # beliebiger Array In [3]: b = a # kein neues Objekt erzeugt In [4]: b is a # gleiches Objekt = Zeiger Out[4]: True In [5]: b[0] = 5 # Modifikation von b ... In [6]: a # ... aendert auch a! Out[6]: array([5, 4, 7]) In [7]: c = a.copy() # "Deep Copy" In [8]: c is a # unterschiedliche Objekte Out[8]: False In [9]: c[0] = 9 In [10]: a # Modifikation von a ... Out[10]: array([5, 4, 7]) In [11]: c # ... aendert c nicht! Out[11]: array([9, 4, 7]) Lineare Algebra --------------- `NumPy`_ enthält eine Vielzahl von Funktionen für die Lineare Algebra: :: In [1]: import numpy as np In [2]: A = np.array([[1.0, 2.0], [3.0, 4.0]]) # 2x2 Matrix In [3]: print(A) [[1. 2.] [3. 4.]] In [4]: A.T # Transponierte Out[4]: array([[1., 3.], [2., 4.]]) In [5]: np.linalg.inv(A) # Inverse Out[5]: array([[-2. , 1. ], [ 1.5, -0.5]]) In [6]: I = np.eye(2) # 2x2 Einheitsmatrix; "eye" fuer "I" In [7]: I Out[7]: array([[1., 0.], [0., 1.]]) In [8]: R = np.array([[0.0, -1.0], [1.0, 0.0]]) # 2x2 Matrix In [9]: R @ R # Matrix Produkt Out[9]: array([[-1., 0.], [ 0., -1.]]) In [10]: np.trace(I) # Spur der Matrix Out[10]: 2.0 In [11]: y = np.array([[5.], [7.]]) # 2x1 Vektor In [12]: y Out[12]: array([[5.], [7.]]) In [13]: x = np.linalg.solve(A, y) # x = A.y (lin. Gleichungssyst.) In [14]: x # Loesungsvektor 2x1 Out[14]: array([[-3.], [ 4.]]) In [15]: np.linalg.eig(A) # Eigenwertanalyse Out[15]: (array([-0.37228132, 5.37228132]), array([[-0.82456484, -0.41597356], [ 0.56576746, -0.90937671]])) Beispiel -------- Gegeben ist folgendes Beispiel aus der Mechanik zum Thema Kräftegleichgewicht. .. figure:: images/exa_numpy.png :scale: 60 % :align: center Gesucht ist die Größe der Kraft :math:`F` im Seil und der Winkel :math:`\alpha`. Von der Mechanik wissen wir dass die Summe der Kraft null ergeben muss. Der Python Code zur Lösung des Beispiels mit NumPy lautet (siehe Beispiel ``beispiel_numpy.py``): .. literalinclude:: src/beispiel_numpy.py :language: python mit der Ausgabe: :: ----------------------------- Beispiel Kraeftegleichgewicht ----------------------------- Author : D.H.Pahr Datum : 12.08.2020 ----------------------------- Ergebnis ~~~~~~~~ F1 = -40.0 N F2 = 80.0 N alpha = 26.57 ° Matplotlib ========== Das Modul `Matplotlib `_ bietet vielseitige :mark:`Plot-Funktionalitäten`. Beispielsweise wurden die Plots in Abbildung `Matplotlib Beispiele`_ damit erstellt. .. _`Matplotlib Beispiele`: .. figure:: images/matplotlib_overview.png :scale: 25 % :align: center `Matplotlib`_ Beispiele Für die Erstellung eines Plots verwenden wir das Untermodul ``pyplot`` welches `Matlab`_ ähnlich aufgebaut ist mittels des Imports: :: import matplotlib.pyplot as plt Einen einfachen Linienplot kann man erzeugen mit: :: In [2]: plt.plot([-1, -4.5, 16, 23, 15, 59]) Out[2]: [] und der Plot wir in Spyder rechts oben dargestellt. .. figure:: images/matplotlib_plot1.png :scale: 70 % :align: center Es gibt eine Vielzahl von Möglichkeiten Plots zu erzeugen. Die wichtigsten sind im folgenden Beispiel zusammengestellt (siehe Beispiek ``matplotlib_plot2.py``). .. literalinclude:: src/matplotlib_plot2.py .. figure:: images/matplotlib_plot2.png :scale: 100 % :align: center Eine umfangreichere Beschreibung der Funktionalität von `Matplotlib`_ findet sich unter: https://www.python-kurs.eu/matplotlib.php Scipy ===== `SciPy `_ :mark:`"Scientific Python"` wird oft im gleichen Atemzug wie NumPy genannt. SciPy erweitert die Leistungsfähigkeit von NumPy. Ein Beispiel ist die Interpolation von Datenpunkten (siehe Beispiel ``scipy_interpolate.py``): .. literalinclude:: src/scipy_interpolate.py Im folgenden Bild sieht man die Datenwerte, sowie eine linear und kubische Interpolation. .. figure:: images/scipy.png :scale: 100 % :align: center Ein weiteres Beispiel ist die numerische Integration. Das folgende Integral .. math:: I(a,b) = \int_0^1 (a\,x^2 + b) dx kann numerisch gelöst werden mittels (siehe Beispiel ``scipy_integrate.py``): .. literalinclude:: src/scipy_integrate.py Die Ausgabe des Python Skriptes lautet :: Ergebnis = 1.6666666666666667 Jupyter ======= `Jupyter `_, genauer gesagt :mark:`Jupyter Notebooks`, * sind Webdokumente für die interaktive Entwicklung und Präsentation von :mark:`wissenschaftlichen Projekten` welche * :mark:`Code und Ausgabe in ein Dokument` integrieren und * Visualisierungen, narrativen Text, mathematische Gleichungen und andere Inhalte kombinieren. Start von Jupyter ----------------- Anaconda fügt unter Windows dem Startmenü Jupyter hinzu. Das Starten dieser App öffnet eine neue Registerkarte im Standard-Webbrowser. .. figure:: images/jupyter1.png :scale: 50 % :align: center ``localhost`` deutet darauf dass es sich um eine lokale Seite und keine Webseite handelt. Mittels Klick auf ``New`` und ``Python 3`` erzeugt man ein neues Notebook. .. figure:: images/jupyter2.png :scale: 70 % :align: center Es wird automatisch ein File ``Untitled.ipynb`` im Arbeitsverzeichnis (Verzeichnis welches die Registerkarte im Standard-Webbrowser anzeigt) gespeichert. Mit ``Save as ..`` kann man dieses File umbenennen. Notebook Interface ------------------ Das Notebook Interface ist ein erweiterter Texteditor welcher neben :mark:`formatierten Text` auch :mark:`ausführbaren Code` enthalten kann. .. figure:: images/jupyter3.png :scale: 70 % :align: center Diese einzelnen Text- oder Code Blöcke ("In [..]") werden als :mark:`Cells` bezeichnet. Die "Computational Engine" (hier der Python Interpreter) welche Code verarbeitet wird als :mark:`Kernel` bezeichnet. Gibt man in die erste Code Cell folgendes ein: :: print("Hello Jupyter") ein und führt diese mit dem ``Run`` Knopf oder ``Ctrl + Enter`` Shortcut aus, erhält man wie in der Abbildung gezeigt eine Ausgabe. Wichtige Befehle bzw. Shortcuts eines Notebooks sind: * Eine :mark:`neue Zelle` erzeugt man mit dem Knopf ``+`` oder ``A`` * Alle :mark:`Tastatur Shortcuts` erhält man durch klicken auf den Tastatur Knopf (rechts oben). * Zwischen :mark:`Edit und Command Mode` wechselt man mit ``Esc`` (blauer Zellrahmen links) und ``Enter`` (grüner Zellrahmen). Im Command Mode (blauer Zellrahmen links) kann man: * mit ``Rauf`` und ``Runter`` Tasten :mark:`zwischen Zellen wechseln` * mit ``A`` oder ``B`` darunter oder darüber :mark:`Zellen erzeugen` * ``M`` wechselt zu einer Markdown (Text) Zelle. * ``Y`` wechselt zu einer Code Zelle. * zweimal ``D`` löscht die aktive Zelle. * ``Z`` macht das Löschen rückgängig * mit ``Shift`` + halten und ``Rauf`` / ``Runter`` Tasten kann man mehrere Zellen markieren, ``Shift`` + ``M`` fügt markierte Zellen zusammen Markdown -------- Im Edit Mode lässt sich einfach ein formatierter Text erzeugen. Gibt man in eine Markdown Zelle folgenden Text ein: :: # Überschrift 1 ## Überschrift 2 Normaler Text mit **fett** und *italic* Text. Ein neuer Paragraph wird durch eine leere Zeile erzeugt. * Manchmal braucht man Aufzählungen * Mit mehreren Punkten. oder 1. nummerierte Listen 2. wie hier dargestellt. [Es ist möglich Hyperlinks einzufügen](https://www.example.com) Inline code wird mittels einfacher Hochkomma erzeugt z.B. `foo()`, und Code Blöcke mit dreifachen Hochkomma: ``` bar() ``` Auch Bilder sind einfach einzufügen: ![Titel](code.png) Formel kann man mittels $\LaTeX$ Syntax einfügen: $$e^{i\pi} + 1 = 0$$ Tabellen einfach so: | Das | ist | |------|---------| | eine | Tabelle | erhält man nach ausführen der Zelle mit ``Ctrl + Enter`` folgendes Ergebnis: .. figure:: images/jupyter4.png :scale: 70 % :align: center und kann damit schön :mark:`formatierte technische Berichte` erzeugen. Kernel ------ Hinter jedem Notebook läuft ein Kernel, bei uns Python 3. Beispielsweise kann man in einer Zelle folgendes definieren: :: import numpy as np def wurzel(x): return x ** 0.5 und danach diese Funktion verwenden d.h.: .. figure:: images/jupyter5.png :scale: 70 % :align: center Dabei gibt es folgendes zu beachten: * Ändert man Werte in einer Zelle, muss man diese mit ``Ctrl + Enter`` neu berechnen. * Möchte man alle Zellen neu berechnen, muss man ``Restart & Run All`` ausführen! Jupyter Beispiel ================ Das Beispiel findet sich unter ``Jupyter_Beispiel.ipynb`` und demonstriert eine ingenieurwissenschaftliche Anwendung aus dem Bereich der Physik. Es wird dabei die Flugbahn eines Projektils untersucht und gleichzeitig ein Berechnungsbericht erstellt. Das folgende Bild zeigt ein Ergebnis dieser Untersuchungen. .. figure:: images/projektil.png :scale: 100 % :align: center Übungsbeispiele ================== **Aufgabe 12.1** In dieser Aufgabe soll die Kraft-Verschiebungs-Kurve einer Probe aus dem Bereich des Biomedical-Engineerings mit Hilfe von Python ausgewertet werden. Im anschließenden Bild sehen Sie die getestete Probe, sowie die zugehörige Kraft-Verschiebungs-Kurve, die mit einer Materialprüfmaschine gemessen wurde. .. figure:: images/Aufgabe_12_1.png :align: center Die Daten für die Verschiebung in mm und Kraft in Newton sind als Listen gegeben: :: # Verschiebung in mm verschiebung = [ 0. , 0.09810706, 0.19972906, 0.30051106, 0.40117306, 0.50129506, 0.60165706, 0.70189806, 0.80202006, 0.90190206, 1.00214806, 1.10196806, 1.20214806, 1.30184806, 1.40196806, 1.50238806, 1.60227806] # Kraft in Newton kraft = [ 10. , 84.3959, 167.649 , 238.241 , 297.654 , 350.267 , 397.334 , 440.565 , 479.436 , 511.907 , 543.733 , 574.285 , 601.333 , 618.475 , 585.185 , 510.965 , 498.479 ] Ziel ist es, die Belastung in kg über die Verschiebung grafisch darzustellen, sowie die maximale Belastung und die Belastung der Probe bei einer Verschiebung von 0.35 mm zu ermitteln. Gehen Sie dafür wie folgt vor: 1) Wandeln Sie die Listen ``verschiebung`` und ``kraft`` in NumPy-Arrays um. 2) Ziehen Sie die Vorlast von 10 Newton von allen Einträgen des ``kraft`` arrays ab. Rechnen Sie anschließend die Belastung von Newton in kg um, indem Sie alle Einträge mit 9.81 (m/s^2) dividieren. Das ``kraft`` array sollte dann wie folgt aussehen: :: In [1]: print(kraft_array_kg) [ 0. 7.58367992 16.07023445 23.26615698 29.32252803 34.68572885 39.48358818 43.89041794 47.85280326 51.16279307 54.40703364 57.52140673 60.27859327 62.02599388 58.63251784 51.0667686 49.79398573] 3) Stellen Sie den Verlauf der Belastung in kg über die Verschiebung in mm dar. Verwenden Sie dafür die ``plot`` Funktion der Bibliothek matplotlib (siehe Kapitel 12.2. im Skriptum). Ihr Ergebnis sollte dann wie folgt aussehen: .. figure:: images/Aufgabe_12_1_results.png :align: center :scale: 70 % 4) Berechnen Sie abschließend die maximale Belastung der Probe in kg sowie die Belastung bei einer Verschiebung von 0.35 mm. Um die Kraft bei einer bestimmten Verschiebung zu berechnen, verwenden Sie ``interp1d`` von Scipy (siehe Kapitel 12.3. Skriptum). Geben Sie dann das Ergebnis per ``print`` Befehl aus. Die Ausgabe sollte dann in etwa wie folgt aussehen: :: Maximale Belastung: 62.03 kg Belastung bei 0.35mm: 26.24 kg Übungsbeispiel für Kolloquium ================== Das folgende Beispiel ist aufbauend und kapitelübergreifend. Es soll Ihnen beim Üben bzw. bei der Vorbereitung für das Kolloquium helfen. Es ist allerdings *nicht* repräsentativ für Art, Umfang oder Schwierigkeitsgrad der Beispiele des Kolloquiums! **Aufgabe K.1** 1) Schreiben Sie eine Klasse ``Haus``. Der Konstruktor bekommt die Argumente ``Adresse`` (string), ``Hoehe`` (float-Zahl) und ``Etagenanzahl`` (integer-Zahl) übergeben und soll sie als Klassenattribute initialiseren. Die Adresse soll als privates Attribut gespeichert werden. 2) Schreiben Sie eine get-Methode, mit der die Adresse abgefragt werden kann. 3) Die Klasse soll eine überladene print-Funktion enthalten, sodass mittels print die Adresse, die Höhe und die Etagenanzahl ausgegeben werden. 4) Ergänzen Sie Ihre Klasse ``Haus`` durch ein statisches Attribut ``Anzahl_Haus``. ``Anzahl_Haus`` soll immer die Anzahl, der jeweils bisher im gesamten Skript erzeugten Instanzen der Klasse zählen. 5) Überladen Sie zusätzlich den ``kleiner``-Vergleichsoperator (``__lt__``). Ein Haus soll als kleiner als ein zweites Haus angesehen werden, wenn es niedriger (Höhe geringer) ist. Überprüfen Sie Ihre Implementierung mit geeignetem Input im Abschnitt 'main'. z.B.: :: In [1]: Haus1 = Haus("Kirchengasse 3",16.27,5) In [2]: print(Haus1) Adresse: Kirchengasse 3, Hoehe: 16.27, Etagenanzahl: 5 In [3]: print(Haus1.Get_Adresse()) Kirchengasse 3 In [4]: print(Haus1.Anzahl_Haus) 1 In [5]: Haus2 = Haus("Kellergasse 1",18.3,6) In [6]: print(Haus1 < Haus2) True In [7]: print(Haus1.Anzahl_Haus) 2 **Aufgabe K.2** 1) Schreiben Sie eine Klasse ``Wolkenkratzer``, die von der Klasse ``Haus`` abgeleitet wird. Sie soll zusätzlich zu den Attributen von ``Haus`` noch das Attribut ``Name`` (string) haben. Wolkenkratzer sind meistens über ihren Namen und nicht über ihre Adresse bekannt (z.B. DC-Tower, Donauturm, Empire-state-building,...). 2) Überladen Sie für die Klasse ``Wolkenkratzer`` die print-Funktion, sodass ein String, der alle Attribute von ``Wolkenkratzer`` enthält, mit print ausgegeben werden kann. 3) Ergänzen Sie Ihre Klasse ``Wolkenkratzer`` durch ein statisches Attribut ``Anzahl_Wolkenkratzer``. ``Anzahl_Wolkenkratzer`` soll immer die Anzahl, der jeweils bisher im gesamten Skript erzeugten Instanzen der Klasse zählen. z.B.: :: In [1]: Wolkenkratzer1 = Wolkenkratzer("Donau-City-Strasse 7",220.3,60,"DC-Tower") In [2]: print(Wolkenkratzer1) Adresse: Donau-City-Strasse 7, Hoehe: 220.3, Etagenanzahl: 60, Name: DC-Tower **Aufgabe K.3** Schreiben Sie in einem neuen python-File eine Funktion ``Read_Haeuser`` (soll keine Klassen-Methode sein!). Dieser Funktion wird der Name einer Textdatei übergeben, die zeilenweise mehrere Häuser enthält. Eine Beispieldatei "Input_Haeuser.txt" ist in TUWEL verfügbar: :: Parkring 18, 18.2, 5 Donau-City-Strasse 7, 220.3, 60, DC-Tower Handelskai 94-96, 171.5, 50, Millenium-Tower Kirchengasse 3, 16.27, 5 1) Importieren Sie Ihre Klassen ``Haus`` und ``Wolkenkratzer``. 2) Die Funktion soll zunächst mit Hilfe einer Ausnahmebehandlung überprüfen, ob das File geöffnet werden kann: * Falls nein, soll die Funktion die Art des Fehlers als string zurück geben. * Falls ja, soll die Textdatei eingelesen werden. 3) Die Textdatei enthält mehrere Zeilen, wobei jede Zeile ein Haus bzw. einen Wolkenkratzer definiert: * Ein Haus wird in der Textdatei wie folgt definiert: Adresse, Höhe, Etagenanzahl * Ein Wolkenkratzer wird in der Textdatei wie folgt definiert: Adresse, Höhe, Etagenanzahl, Name Lesen Sie das File ein und erstellen Sie eine Liste aus Haus- und Wolkenkratzer-Objekten, wobei jede Zeile des Files zur Initialisierung eines Haus- oder eines Wolkenkratzer-Objektes verwendet werden soll. 4) Danach soll diese Hausliste mit Hilfe des überladenen ``kleiner``-Operators aufsteigend sortiert werden, d.h. nach der Höhe sortiert werden. Die sortierte Liste der Häuser soll zurück gegeben werden. .. hint :: * Die Python-Funktionen ``sort`` und ``sorted`` verwenden den kleiner-Operator zum Sortieren. * Damit der überladene print-Operator zum Ausgeben der Liste von Häusern und Wolkenkratzern verwendet wird, muss eine Schleife über die Liste verwendet werden. * Sie können auch testen, ob Ihr Skript noch funktioniert wenn Sie versuchen eine nicht existierende Datei oder die Datei "Input_Haueser2.txt" aus TUWEL einzulesen. Beispiel für Nutzung der Funktion und Ausgabe: :: In [1]: Gebaeude_Liste = Read_Haeuser("Input_Haeuser.txt") In [2]: for i in Gebaeude_Liste: ...: print(i) Adresse: Kirchengasse 3, Hoehe: 16.27, Etagenanzahl: 5 Adresse: Parkring 18, Hoehe: 18.2, Etagenanzahl: 5 Adresse: Handelskai 94-96, Hoehe: 171.5, Etagenanzahl: 50, Name: Millenium-Tower Adresse: Donau-City-Strasse 7, Hoehe: 220.3, Etagenanzahl: 60, Name: DC-Tower