Wikibooks dewikibooks https://de.wikibooks.org/wiki/Hauptseite MediaWiki 1.47.0-wmf.6 first-letter Medium Spezial Diskussion Benutzer Benutzer Diskussion Wikibooks Wikibooks Diskussion Datei Datei Diskussion MediaWiki MediaWiki Diskussion Vorlage Vorlage Diskussion Hilfe Hilfe Diskussion Kategorie Kategorie Diskussion Regal Regal Diskussion TimedText TimedText talk Modul Modul Diskussion Veranstaltung Veranstaltung Diskussion Traktorenlexikon: AGCO 0 10725 1088119 1016208 2026-06-13T18:37:31Z Baupit 56622 /* AGCO White */ 1088119 wikitext text/x-wiki {{:Traktorenlexikon: Navigation}} Die '''AGCO-Corporation''' ist ein Hersteller von Landmaschinen mit Sitz in Duluth (Georgia), USA. ==Geschichte== Das Unternehmen entstand '''1990''' als leitende Angestellte von [[Traktorenlexikon: Deutz-Allis|Deutz-Allis]] die nordamerikanischen Aktivitäten von Klöckner-Humboldt-Deutz mit der Landmaschinen-Marke [[Traktorenlexikon: Deutz-Fahr|Deutz Fahr]] erwarben. Es wurde dann umbenannt in '''A'''llis-'''G'''leaner '''Co'''rporation, kurz '''AGCO'''. Gleaner wurde die Marke für Mähdrescher, während die Produktlinie der Traktoren seit 2001 AGCO Tractors heißt. In den folgenden Jahren wurde zahlreiche Unternehmen und Aktivitäten hinzu erworben: '''1991''' die Hesston Corporation, ein Hersteller von Erntemaschinen, der wiederum zu 50 % an [[Traktorenlexikon: Case-IH|Case International]] beteiligt war, die [[Traktorenlexikon: White | White Tractor]] Produktlinie von New White Idea, einer Tochtergesellschaft der Allied Corporation, und 1993 den Rest von New White Idea. '''1993''' Vertriebsrechte von [[Traktorenlexikon: Massey Ferguson|Massey Ferguson]] für Nordamerika. '''1994''' McConnell Tractors, Hersteller der [[Traktorenlexikon: Massey Ferguson|Massey Ferguson]] Knicklenker '''1994''' Sämaschinen-Produktlinie Black Machine '''1995''' AgEquipment Group mit den Marken Glenco, Tye und Farmhand '''1996''' Iochpe-Maxion in Brasilien mit der Marke und Produktion von [[Traktorenlexikon: Massey Ferguson|Massey Ferguson]] für diese Region. '''1996''' [[Traktorenlexikon: Deutz|Deutz]] Argentina, Marktkführer für Traktoren in Argentinien '''1996''' Western Combine Corporation und Portage Manufacturing in Kanada '''1997''' [[Traktorenlexikon: Fendt|Fendt]] in Deutschland '''1997''' Dronninborg Industries in Dänemark, Hersteller von Massey-Ferguson Mähdreschern '''1998''' Gründung eines Unternehmens zusammen mit der [[Traktorenlexikon: Deutz|Deutz AG]] zur Produktion von Maschinen in Argentinien. '''2002''' [[Traktorenlexikon: Challenger|Challenger]]-Produktlinie von [[Traktorenlexikon: Caterpillar|Caterpillar]] '''2004''' [[Traktorenlexikon: Valtra|Valtra]] Tractor von der finnischen Kone Gruppe mit Lizenzproduktion von [[Traktorenlexikon: Eicher|Eicher]] in Indien '''2011''' AGCO übernimmt Laverda mit den Fella Werke GmbH in Feucht von [[Traktorenlexikon: ARGO|ARGO]] mit deren Grünlandtechnik '''2017''' AGCO will seine Marken [[Traktorenlexikon: Fendt|Fendt]] und [[Traktorenlexikon: Massey Ferguson|Massey Ferguson]] zu sogenannten Fulllinern ausbauen und übernimmt die Erntetechnik von Lely <ref>https://www.profi.de/news/Agco-kauft-Erntetechnik-von-Lely-8014499.html</ref><ref>https://www.agrarheute.com/technik/agco-uebernimmt-lely-gruenfuttertechnik-fendt-lely-haus-532712</ref> ==Typen== [[Datei:AGCO tractor, Stirling Tractor Parade 2008.jpg|thumb|AGCO-Traktor in Kanada 2008]] Es werden/wurden Schlepper mit folgenden Typenbezeichnungen vertrieben: ===ST Series Kompakt-Traktoren (25–55 PS)=== {{:Traktorenlexikon:_Create|ST25}} {{:Traktorenlexikon:_Create|ST30x}} {{:Traktorenlexikon:_Create|ST30x Hydro}} {{:Traktorenlexikon:_Create|ST32}} ===GT Series Universal-Traktoren (70–85 PS)=== {{:Traktorenlexikon:_Create|GT45A}} {{:Traktorenlexikon:_Create|GT55A}} {{:Traktorenlexikon:_Create|GT65A}} {{:Traktorenlexikon:_Create|GT75A}} ===LT Series Mittelklasse-Traktoren (75–85 PS)=== {{:Traktorenlexikon:_Create|LT75}} {{:Traktorenlexikon:_Create|LT85}} ===RT Series Traktoren (100–150 PS)=== {{:Traktorenlexikon:_Create|RT100}} {{:Traktorenlexikon:_Create|RT120}} {{:Traktorenlexikon:_Create|RT135}} {{:Traktorenlexikon:_Create|RT150}} ===DT Series Traktoren (180–240 PS)=== [[Datei:AGCO DT 220 Tractor Freedom Township Michigan.JPG|thumb|AGCO DT 220]] {{:Traktorenlexikon:_Create|DT180}} {{:Traktorenlexikon:_Create|DT200}} {{:Traktorenlexikon:_Create|DT220}} {{:Traktorenlexikon:_Create|DT240}} ==weitere AGCO-Schleppermarken== Unter dem AGCO-Dach werden/wurden Schlepper der folgenden Marken vertrieben: *[[Traktorenlexikon: AGCO-Allis|AGCO-Allis]] *[[Traktorenlexikon: Challenger|Challenger]] *[[Traktorenlexikon: Fendt|Fendt]] *[[Traktorenlexikon: Iseki|Iseki]] *[[Traktorenlexikon: Massey Ferguson|Massey Ferguson]] *[[Traktorenlexikon: Valtra|Valtra]] *[[Traktorenlexikon: White|White Tractor]] ==Weblinks== {{Commons|Category:AGCO tractors|AGCO-Traktoren}} {{Wikipedia1|AGCO}} * [http://www.agcotractors.agcocorp.com Homepage von AGCO Tractors] * [http://www.agcocorp.com Homepage der AGCO] {{:Traktorenlexikon: Navigation}} 4cg3akteekr1hawozq3l9d0m7qccicl 1088134 1088119 2026-06-14T10:45:50Z ~2026-34728-60 116391 /* LT Series Mittelklasse-Traktoren (75–85 PS) */ 1088134 wikitext text/x-wiki {{:Traktorenlexikon: Navigation}} Die '''AGCO-Corporation''' ist ein Hersteller von Landmaschinen mit Sitz in Duluth (Georgia), USA. ==Geschichte== Das Unternehmen entstand '''1990''' als leitende Angestellte von [[Traktorenlexikon: Deutz-Allis|Deutz-Allis]] die nordamerikanischen Aktivitäten von Klöckner-Humboldt-Deutz mit der Landmaschinen-Marke [[Traktorenlexikon: Deutz-Fahr|Deutz Fahr]] erwarben. Es wurde dann umbenannt in '''A'''llis-'''G'''leaner '''Co'''rporation, kurz '''AGCO'''. Gleaner wurde die Marke für Mähdrescher, während die Produktlinie der Traktoren seit 2001 AGCO Tractors heißt. In den folgenden Jahren wurde zahlreiche Unternehmen und Aktivitäten hinzu erworben: '''1991''' die Hesston Corporation, ein Hersteller von Erntemaschinen, der wiederum zu 50 % an [[Traktorenlexikon: Case-IH|Case International]] beteiligt war, die [[Traktorenlexikon: White | White Tractor]] Produktlinie von New White Idea, einer Tochtergesellschaft der Allied Corporation, und 1993 den Rest von New White Idea. '''1993''' Vertriebsrechte von [[Traktorenlexikon: Massey Ferguson|Massey Ferguson]] für Nordamerika. '''1994''' McConnell Tractors, Hersteller der [[Traktorenlexikon: Massey Ferguson|Massey Ferguson]] Knicklenker '''1994''' Sämaschinen-Produktlinie Black Machine '''1995''' AgEquipment Group mit den Marken Glenco, Tye und Farmhand '''1996''' Iochpe-Maxion in Brasilien mit der Marke und Produktion von [[Traktorenlexikon: Massey Ferguson|Massey Ferguson]] für diese Region. '''1996''' [[Traktorenlexikon: Deutz|Deutz]] Argentina, Marktkführer für Traktoren in Argentinien '''1996''' Western Combine Corporation und Portage Manufacturing in Kanada '''1997''' [[Traktorenlexikon: Fendt|Fendt]] in Deutschland '''1997''' Dronninborg Industries in Dänemark, Hersteller von Massey-Ferguson Mähdreschern '''1998''' Gründung eines Unternehmens zusammen mit der [[Traktorenlexikon: Deutz|Deutz AG]] zur Produktion von Maschinen in Argentinien. '''2002''' [[Traktorenlexikon: Challenger|Challenger]]-Produktlinie von [[Traktorenlexikon: Caterpillar|Caterpillar]] '''2004''' [[Traktorenlexikon: Valtra|Valtra]] Tractor von der finnischen Kone Gruppe mit Lizenzproduktion von [[Traktorenlexikon: Eicher|Eicher]] in Indien '''2011''' AGCO übernimmt Laverda mit den Fella Werke GmbH in Feucht von [[Traktorenlexikon: ARGO|ARGO]] mit deren Grünlandtechnik '''2017''' AGCO will seine Marken [[Traktorenlexikon: Fendt|Fendt]] und [[Traktorenlexikon: Massey Ferguson|Massey Ferguson]] zu sogenannten Fulllinern ausbauen und übernimmt die Erntetechnik von Lely <ref>https://www.profi.de/news/Agco-kauft-Erntetechnik-von-Lely-8014499.html</ref><ref>https://www.agrarheute.com/technik/agco-uebernimmt-lely-gruenfuttertechnik-fendt-lely-haus-532712</ref> ==Typen== [[Datei:AGCO tractor, Stirling Tractor Parade 2008.jpg|thumb|AGCO-Traktor in Kanada 2008]] Es werden/wurden Schlepper mit folgenden Typenbezeichnungen vertrieben: ===ST Series Kompakt-Traktoren (25–55 PS)=== {{:Traktorenlexikon:_Create|ST25}} {{:Traktorenlexikon:_Create|ST30x}} {{:Traktorenlexikon:_Create|ST30x Hydro}} {{:Traktorenlexikon:_Create|ST32}} ===GT Series Universal-Traktoren (70–85 PS)=== {{:Traktorenlexikon:_Create|GT45A}} {{:Traktorenlexikon:_Create|GT55A}} {{:Traktorenlexikon:_Create|GT65A}} {{:Traktorenlexikon:_Create|GT75A}} ===LT Series Mittelklasse-Traktoren (75–85 PS)=== {{:Traktorenlexikon:_Create|LT 70}} {{:Traktorenlexikon:_Create|LT 75}} {{:Traktorenlexikon:_Create|LT 85}} {{:Traktorenlexikon:_Create|LT 90}} ===RT Series Traktoren (100–150 PS)=== {{:Traktorenlexikon:_Create|RT100}} {{:Traktorenlexikon:_Create|RT120}} {{:Traktorenlexikon:_Create|RT135}} {{:Traktorenlexikon:_Create|RT150}} ===DT Series Traktoren (180–240 PS)=== [[Datei:AGCO DT 220 Tractor Freedom Township Michigan.JPG|thumb|AGCO DT 220]] {{:Traktorenlexikon:_Create|DT180}} {{:Traktorenlexikon:_Create|DT200}} {{:Traktorenlexikon:_Create|DT220}} {{:Traktorenlexikon:_Create|DT240}} ==weitere AGCO-Schleppermarken== Unter dem AGCO-Dach werden/wurden Schlepper der folgenden Marken vertrieben: *[[Traktorenlexikon: AGCO-Allis|AGCO-Allis]] *[[Traktorenlexikon: Challenger|Challenger]] *[[Traktorenlexikon: Fendt|Fendt]] *[[Traktorenlexikon: Iseki|Iseki]] *[[Traktorenlexikon: Massey Ferguson|Massey Ferguson]] *[[Traktorenlexikon: Valtra|Valtra]] *[[Traktorenlexikon: White|White Tractor]] ==Weblinks== {{Commons|Category:AGCO tractors|AGCO-Traktoren}} {{Wikipedia1|AGCO}} * [http://www.agcotractors.agcocorp.com Homepage von AGCO Tractors] * [http://www.agcocorp.com Homepage der AGCO] {{:Traktorenlexikon: Navigation}} 5yt3s9kamdermyly6e01ol32iq70nn5 Pseudoprimzahlen: Starke Pseudoprimzahlen 0 11543 1088104 1088077 2026-06-13T16:30:24Z Hardy42 1911 /* Definition */ typo 1088104 wikitext text/x-wiki {{Navigation_Buch|Pseudoprimzahlen|Pseudoprimzahlen}} Jede ungerade Primzahl <math>n</math> erfüllt mit jeder teilerfremden ganzzahligen Basis <math>a</math> eine der [[Pseudoprimzahlen:_Kongruenz_und_Restklassen#Kongruenz|Kongruenzen]] {{Formel2|Gl1|<math> a^d \equiv 1 \pmod n\quad</math>mit <math>\ n - 1 = d \cdot 2^s\ </math> und <math>d\ </math> ungerade|1}}{{Formel2|Gl2|<math> a^{d \cdot 2^r} \equiv -1 \pmod n \quad</math>für ein <math>r</math> mit <math>0 \leqq r < s</math>.|2}} Anmerkung: Kongruenz [[#Gl2|(2)]] ist gleichbedeutend mit <math> a^{d \cdot 2^r} \equiv n - 1 \pmod n </math>. ==Definition== Eine ungerade Zahl <math>n</math> ist '''starke Pseudoprimzahl''' zur Basis <math>a</math>, wenn sie zusammengesetzt ist, und mit einer ganzzahligen Basis <math>a</math>, für die <math>1 < a < n-1 \pmod n</math> gilt, eine der obengenannten Kongruenzen erfüllt ist. In dieser Hinsicht verhält sie sich wie eine Primzahl. <br> Mit <math>a = 1</math> erfüllen ''alle'' ganzen Zahlen Kongruenz [[#Gl1|(1)]] und mit <math>a = n - 1</math> ''alle'' ungeraden Zahlen Kongruenz [[#Gl2|(2)]], solche Basen eignen sich also nicht zur Erkennung zusammengesetzter Zahlen. Die obengenannten Kongruenzen werden im Miller-Rabin-Test<ref>{{w|Miller-Rabin-Test|Miller-Rabin-Test}}</ref> genutzt, um zu prüfen ob eine ungerade Zahl (wahrscheinlich) eine Primzahl oder sicher keine ist; dabei kann durch Tests mit mehreren Basen die Wahrscheinlichkeit, dass eine Pseudoprimzahl den Test besteht, reduziert werden. Starke Pseudoprimzahlen zur Basis <math>a</math> sind also zusammengesetzte Zahlen, die den Miller-Rabin-Test mit der Basis <math>a</math> bestehen. ==Eigenschaften== Im Gegensatz zu den [[Pseudoprimzahlen: Fermatsche Pseudoprimzahlen| Fermatschen Pseudoprimzahlen]] gibt es keine starken Pseudoprimzahlen, die zu jeder teilerfremden Basis pseudoprim sind; eine Entsprechung zu den [[Pseudoprimzahlen: Absolute Fermatsche Pseudoprimzahlen#Carmichael-Zahlen|Carmichael-Zahlen]] gibt es also nicht, und es ist auch nicht jede zusammengesetzte ungerade Zahl starke Pseudoprimzahl zu irgendeiner Basis 1 < a < n-1. * Alle zusammengesetzten Mersennezahlen (<math>2^p - 1</math> mit <math>\ p</math> = prim) sind starke Pseudoprimzahlen zur Basis 2. * Alle zusammengesetzten Fermatzahlen (<math>2^{2^n} + 1</math>) sind starke Pseudoprimzahlen zur Basis 2.<ref>{{w|en:Fermat Numbers#Pseudoprimes and Fermat numbers|Pseudoprimes and Fermat numbers}}</ref> * <math>(4^p + 1) / 5</math> ist starke Pseudoprimzahl zur Basis 2, wenn <math>p > 5</math> und eine Primzahl ist.<ref>Prime Numbers - A Computational Perspective, Richard Crandall & Carl Pomerance, Springer Verlag, ISBN 0-387-25282-7 http://thales.doa.fmph.uniba.sk/macaj/skola/teoriapoli/primes.pdf</ref> * Die Quadrate der [[w:Wieferich-Primzahl|Wieferich-Primzahl]]en sind starke Pseudoprimzahlen zur Basis 2. === Bezug zu Eulerschen Pseudoprimzahlen === Jede starke Pseudoprimzahl <math>n\ </math> zur Basis <math>a\ </math> ist auch eine [[Pseudoprimzahlen: Eulersche Pseudoprimzahlen| Eulersche Pseudoprimzahl]] zur Basis <math>a\ </math>. Warum? <br> Für eine eulersche Pseudoprimzahl zur Basis <math>a\ </math> gilt <math>a^\frac{n-1}{2} \equiv \pm 1 \pmod n</math>. Mit der oben beschriebenen Zerlegung <math>\ n-1 = d\cdot 2^s\ </math> ist <math>\ a^{\frac{n-1}{2}} = a^{d\cdot 2^{s-1}}</math>. Bei starken Pseudoprimzahlen gibt es zwei Fälle: # Kongruenz 1 ist erfüllt: <math>a^d \equiv 1 \pmod n</math>, <br>dann ist <math>a^{d\cdot 2^{s-1}} = (a^d)^{2^{s-1}} \equiv 1^{2^{s-1}} \pmod n = 1</math> und damit <math>a^{\frac{n-1}{2}} \equiv 1 \pmod n</math>. # Kongruenz 2 ist erfüllt: <math>a^{d\cdot 2^r} \equiv -1 \pmod n </math>, <br>dann ist <math>a^{d\cdot 2^{s-1}} = (a^{d\cdot 2^r})^{2^{s-r-1}} \equiv (-1)^{2^{s-r-1}} \pmod n \equiv \pm 1</math> und damit <math>a^{\frac{n-1}{2}} \equiv \pm 1 \pmod n</math>. Jede eulersche Pseudoprimzahl der Form <math>\ n = 4\cdot k + 3\ </math> ist starke Pseudoprimzahl: <br>in diesem Fall ist <math>\ d = \frac{n-1}{2}</math> und <math>\ s = 1\ </math> und damit <math>\ a^d = a^{\frac{n-1}{2}} = a^{d\cdot 2^r}</math>; mit <math>\ a^{\frac{n-1}{2}} \equiv \pm 1 \pmod n\ </math> ist also eine der obengenannten Kongruenzen erfüllt. <!-- Wenn <math>a^{\frac{n-1}{2}} = a^{d\cdot 2^0}</math> ist, und <math>a^{d\cdot 2^r} \equiv -1 \mod n</math> mit <math> 0 \le r \le (s-1)</math> gilt, dann gilt auch <math>a^{\frac{n-1}{2}} \equiv -1 \mod n</math>. --> ===Teiler=== Für alle Primteiler <math>p</math> einer starken Pseudoprimzahl <math>n\ </math>gilt *<math> n \equiv 1 \pmod {l_a(p)}</math> und damit *<math> n \equiv p \pmod {p \cdot l_a(p)}</math>, wobei <math>l_a(p)</math> die multiplikative Ordnung von <math>p</math> zur Basis <math>a</math> ist.<ref>[https://www.ams.org/mcom/1980-35-151/S0025-5718-1980-0572872-7/S0025-5718-1980-0572872-7.pdf The pseudoprimes to 25 * 10^9]</ref> <ref>[[Pseudoprimzahlen: Glossar|Glossar]]</ref><ref>{{w|en:Multiplicative order|Multiplicative order}}</ref> Die höchste Zweierpotenz, durch die die multiplikative Ordnung eines Primteilers teilbar ist, ist für alle Teiler einer starken Pseudoprimzahl gleich, d. h. alle multiplikativen Ordnungen der Primteiler derselben starken Pseudoprimzahl sind Produkt einer ungeraden Zahl und derselben Zweierpotenz (einschließlich <math>2^0</math>).<ref>[https://digitalcommons.iwu.edu/cgi/viewcontent.cgi?article=1024&context=math_honproj New Implementations for Tabulating Pseudoprimes and Liars.pdf]</ref> Starke Pseudoprimzahlen sind weitgehend quadratfrei; nur die Quadrate der Wieferich-Primzahlen zur Basis <math>a</math> sind starke Pseudoprimzahlen zur Basis <math>a</math> und können Teiler einer solchen sein. Mit der Basis 2 sind das die Wieferich-Primzahlen 1093 und 3511 (weitere sind bisher nicht bekannt). Starke Pseudoprimzahlen lassen sich häufig als Produkte der Form {| |- |<math>\ q = (2\cdot k \cdot x + 1) \cdot (2 \cdot m \cdot x + 1)\ </math> mit |- | <math>\quad x \in \N</math>, |- | <math>\quad \gcd(k, m) = 1\ </math>und |- | <math>\quad 0 < k \ll q,\ 0 < m \ll q\ </math> |} ausdrücken. Umgekehrt ist der Anteil starker Pseudoprimzahlen bei solchen Produkten deutlich höher als bei ungeraden Zahlen allgemein. === Anzahl der Basen === Wenn die Primteiler<math>\ p_i\ </math>einer zusammengesetzten Zahl <math>n</math> bekannt sind, kann die Anzahl der Basen, zu denen sie starke Pseudoprimzahl ist, berechnet werden: Mit der Faktorisierung {{Formel2 | |<math>n = \prod_{i=1}^k p_i^{\alpha_i}</math>| }} ist die Anzahl der Basen <math><n</math> einschließlich 1 und n-1 {{Formel2 | |<math>\left(1 + \frac{2^{k\nu}-1}{2^k-1}\right) \prod_{i=1}^k\gcd\left(d,\ p_i^'\right)</math><ref>{{:Pseudoprimzahlen: Vorlage:Anzahl_falscher_Zeugen}}</ref>| }} Dabei ist * <math>\ d</math> der größte ungerade Teiler von <math>\ n - 1</math> [[#Gl1|(wie oben)]] * <math>\ p_i^'</math> der größte ungerade Teiler von <math>\ p_i - 1</math>, * <math>\ \nu_2(p_i) </math> der größte Exponent, mit dem <math>\ 2^{\nu_2(p_i)}|p_i-1\ </math>gilt, so dass <math>\ p_i - 1 = 2^{\nu_2(p_i)} \cdot p_i^'\ </math>ist und * <math>\ \nu</math> das Minimum aller <math> \nu_2(p_i)</math>. Basen, zu denen eine zusammengesetze Zahl <math>n\ </math>keine Pseudoprimzahl ist, werden Zeugen für die Zusammengesetztheit der Zahl genannt; solche zu denen sie pseudoprim ist, werden „falsche Zeugen“ genannt. Maximal ein Viertel der teilerfremden Basen zwischen 0 und n sind falsche Zeugen. ===Häufigkeit=== Starke Pseudoprimzahlen zu einer festen Basis sind viel seltener als Primzahlen. Folgende Tabelle zeigt die Anzahl starker Pseudoprimzahlen zur Basis 2 im Vergleich zur Anzahl fermatscher Pseudoprimzahlen zur Basis 2 und der Primzahlen bis zur angegebenen Obergrenze. {| class="wikitable" style="text-align:right;" |+ Anzahl Starker Pseudoprimzahlen zur Basis 2 im Vergleich zu Primzahlen |- ! Obergrenze !! Primzahlen<ref>{{w|Primzahlsatz#Zahlenbeispiele|Primzahlsatz}}</ref> !! Fermatsche Pseudoprimzahlen !! Starke Pseudoprimzahlen<ref>{{:Pseudoprimzahlen: Vorlage:Statistics}}</ref> |- | <math>10^9\ </math> || 50.847.534 || 5.597 || 1.282 |- | <math>10^{12}</math> || 37.607.912.018 || 101.629 || 22.407 |- | <math>10^{15}</math> || 29.844.570.422.669 || 1.801.533 || 419.489 |- | <math>10^{18}</math> || 24.739.954.287.740.860 || 33.763.684 ||8.646.507 |} ==== Restklassen ==== Starke Pseudoprimzahlen liegen wesentlich häufiger in der Restklasse 1 modulo kleiner Zahlen <math>(m)</math> als in anderen Restklassen. Folgende Tabelle zeigt die Verteilung der starken Pseudoprimzahlen zur Basis 2 bis 10<sup>15</sup> auf Restklassen modulo m an ein paar Beispielen. {|class="wikitable" style="text-align:right;" ! !! colspan="8" | Anzahl starker Pseudoprimzahlen zur Basis 2 in Restklasse |- ! m !! style="text-align:center"| 0 !! style="text-align:center"| 1 !! style="text-align:center"| 2 !! style="text-align:center"| 3 !! style="text-align:center"| 4 !! style="text-align:center"| 5 !! style="text-align:center"| 6 !! style="text-align:center"| 7 |- |3 || 26 || 327193 || 92270 |- |5 || 1373 || 199879 || 84716 || 74590 || 58931 |- |7 || 370 || 142844 || 49224 || 62834 || 47482 || 57853 || 58882 |- |8 || 0 || 207103 || 0 || 44885 || 0 || 122473 || 0 || 45028 |} ==Beispiele== Um zu zeigen, wie unterschiedlich starke Pseudoprimzahlen ausfallen können, werden als Beispiele die drei Zahlen 781, 1541 und 25 gezeigt. An der Zahl 781 wird erstmal die Zerlegung in eine 2er-Potenz und eine ungerade Zahl gezeigt: '''Zerlegung''' Zu einer Zahl <math>n\ </math> wird das ungerade <math>d\ </math> gesucht, so dass <math>n = d\cdot 2^s+1\ </math> gilt. Die Formel können wir umstellen: *<math>n = d\cdot 2^s+1</math> *<math>n-1 = d\cdot 2^s</math> *<math>\frac{n-1}{2^s} = d</math> Am Beispiel der Zahl <math>n = 781\ </math> sieht das so aus: Die größte 2er-Potenz, durch die <math>n-1 = 780\ </math> teilbar ist, ist <math>2^2 = 4</math>; der andere Faktor ist dann <math>d=780/4=195 </math>. Es wird also nicht die vollständige Primfaktorzerlegung <math>781-1 = 2^2\cdot 3\cdot 5\cdot 13\ </math> gebraucht. '''781 zur Basis 5''' <math>a = 5</math> <math>n = 781 \ \ \ d = 195\ </math> <math>5^{195} \equiv 1 \pmod {781}</math> also ist 781 eine starke Pseudoprimzahl zur Basis 5. '''1541 zur Basis 5''' <math>a = 5</math> <math>n = 1541 \ \ \ d = 385\ </math> <math>5^{385} \equiv 1540 \pmod {1541}</math> also ist 1541 eine starke Pseudoprimzahl zur Basis 5. '''25 zur Basis 7''' <math>a = 7</math> <math>n = 25 \ \ \ d = 3\ </math> <math>7^3 \equiv 18 \pmod {25}</math> <math>7^6 \equiv 24 \pmod {25}</math> also ist 25 eine starke Pseudoprimzahl zur Basis 7. '''305 zur Basis 11''' Dies ist ein Gegenbeispiel. <math>a = 11</math> <math>n = 305 \ \ \ d = 19\ </math> <math>11^{19} \equiv 111 \pmod {305}</math> <math>11^{38} \equiv 121 \pmod {305}</math> <math>11^{76} \equiv 1 \pmod {305}</math> Somit ist 305 keine starke Pseudoprimzahl zur Basis 11. == Quellen == <references/> {{Navigation Text| |links= [[Pseudoprimzahlen: Fermatsche Pseudoprimzahlen| Fermatsche Pseudoprimzahlen]] |mitte= [[Pseudoprimzahlen: Eulersche Pseudoprimzahlen| Eulersche Pseudoprimzahlen]] |rechts= [[Pseudoprimzahlen: Starke Pseudoprimzahlen| Starke Pseudoprimzahlen]] }} p3smy20bykeq6q9lct91ztu7krpdmty Berechnung einer akustischen Transmissionline 0 90016 1088120 1088055 2026-06-13T22:53:23Z Bautsch 35687 /* Quantitative Berechnung */ Massendichte 1088120 wikitext text/x-wiki {{Regal | ort=Physik}} {{Infoleiste|Physik|Studium|S|Technik}} [[Bild:Transmission-line.png|rechts|mini|Beispiel für eine Transmissionline als akustischen Kanal (blaue Pfeile) für den rückwärtigen Schall eines Lautsprechers (orangefarben) in einer Lautsprecherbox (dunkelbraun).]] == Einführung == '''Transmissionline''' ("Übertragungsleitung") nennt man in der Lautsprecherakustik einen in der Regel mehrfach gefalteten Kanal, durch den der von einem Lautsprecher nach hinten abgegebene Schall hindurchtritt. Am Ende dieses Kanals befindet sich eine Öffnung, durch die der Schall heraustreten kann. Dieser Schall kann unter geeigneten Voraussetzungen dazu verwendet werden, den Amplituden-Frequenzgang einer elektrodynamischen Lautsprecherbox am tieffrequenten Ende zu erweitern und zu verbessern. Ausgehend von idealisierten theoretischen Ansätzen zum Abstrahlverhalten von Lautsprechern und mit der Theorie der quasihomogenen Absorber wird in diesem Buch darauf eingegangen, wie der Tieftonbereich einer Transmissionline-Lautsprecherbox dimensioniert und akustisch gedämpft werden kann. Dazu werden wegen des einfachen mathematischen Formalismus Funktionen komplexer Zahlen ({{w|Eulersche Formel}}) herangezogen. Dieses Wikibook basiert auf der privaten Vorveröffentlichung ''Anleitung zur akustischen Berechnung einer elektrodynamischen "transmission line"-Lautsprecherbox'' aus dem Jahr 2000. Das hier vorgestellte Berechnungsverfahren wurde vom Autor im Jahr 1986 entwickelt und für den Bau von Transmissionline-Lautsprecherboxen angewandt. <div style="clear:both"></div> == Abstrahlverhalten von Lautsprechern == [[Bild:Amplitudenfrequenzgang.Lautsprecher.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang eines elektrodynamischen Lautsprechers.]] Der Amplitudenfrequenzgang <math>A_0</math> eines elektrodynamischen Lautsprechers mit der Gesamtgüte <math>Q</math> und der Eigenfrequenz <math>f_0</math> berechnet sich über der Schallfrequenz <math>f</math> theoretisch zu :<math>A_0 (f) = \frac {\frac {f} {f_0}} {\sqrt {\frac {1} {Q^2} + \left(\frac {f} {f_0} - \frac {f_0} {f}\right)^2}}</math> Im Resonanzfall <math>f = f_0</math> vereinfacht sich diese Gleichung zu :<math>A_0 (f_0) = Q</math> Mit zunehmender Frequenz fällt der Frequenzgang jedoch entgegen der Aussage obiger Gleichung wieder ab, da die Lautsprechermembran wegen ihrer mechanischen Trägheit nicht mehr mitschwingen kann. Weitere Abweichungen ergeben sich in der Praxis, weil auch andere Eigenfrequenzen als <math>f_0</math> noch kleine Beiträge zum Frequenzgang leisten. Diese Eigenfrequenzen werden im Allgemeinen durch ungewollte Schwingungsmoden der Lautsprechermembran verursacht. <div style="clear:both"></div> === Freier Lautsprecher === [[Bild:Freier.Lautsprecher.gif|upright=2|miniatur|rechts|Freier Lautsprecher.]] Wird ein Lautsprecher bei der Frequenz <math>f</math> ohne Schallwand oder Gehäuse betrieben, so interferieren an der Stelle <math>x = 0</math> (also an der Lautsprecheroberfläche) über der Zeit <math>t</math> die nach vorne abgegebene Schallwelle :<math>A_v = A(f,t) = A_0(f) \cdot e^{-i \omega t}</math> und die nach hinten abgestrahlte Schallwelle :<math>A_h = -A(f,t) = -A_0(f) \cdot e^{-i \omega t}</math> mit der {{w|Eulersche Zahl|Eulerschen Zahl}} <math>e</math>, der {{w|Imaginäre Zahl|imaginären Einheit}} <math>i = \sqrt {-1}</math> und der {{w|Kreisfrequenz}} <math>\omega = 2 \pi f</math> zu allen Zeitpunkten destruktiv: :<math>A_{ges} = A_v + A_h = 0</math> Dies bedeutet, dass die gesamte Amplitudenfrequenzgang <math>A_{ges}</math> null wird, da sich die beiden Schallwellen gegenseitig auslöschen. Dies wird auch als {{w|Akustischer Kurzschluss|akustischer Kurzschluss}} bezeichnet. <div style="clear:both"></div> === Lautsprecher mit Schallwand === [[Bild:Lautsprecher.mit.Schallwand.gif|upright=2|miniatur|rechts|Lautsprecher mit unendlich ausgedehnter Schallwand.]] Die Probleme beim freien Lautsprecher können umgangen werden, wenn der Lautsprecher in eine im Verhältnis zur größten abzustrahlenden Schallwellenlänge möglichst weit ausgedehnte Schallwand eingebaut wird. Allerdings muss eine solche Schallwand meist sehr große Abmessungen haben. Bei einer minimalen Frequenz von beispielsweise 30&nbsp;Hertz müsste die Schallwand seitlich mehrere Meter Ausdehnung haben. :<math>A_{ges} = A_v = A(f,t) = A_0(f)\cdot e^{-i \omega t}</math> wobei <math>A_v</math> die nach vorne abgestrahlte Schallwelle repräsentiert, die in diesem Fall mit der gesamten abgestrahlten <math>A_{ges}</math> identisch ist. <div style="clear:both"></div> === Schallabyrinth === [[Bild: Schallabyrinth.gif|upright=2|miniatur|rechts|Schalllabyrinth.]] Eine Alternative wäre der Einbau des Lautsprechers in ein sogenanntes '''Schallabyrinth'''. Die Schallenergie der nach hinten abgegebenen Schallwelle wird hier durch Mehrfachreflexion und Mehrfachstreuung in Verbindung mit Absorption vernichtet. Auch ein solches Schallabyrinth muss jedoch sehr groß dimensioniert werden, damit der Schall weitgehend absorbiert werden kann. Wie auch bei der Schallwand besteht der Nachteil, dass die vom Lautsprecher nach hinten abgestrahlte Schallenergie nicht genutzt wird. <div style="clear:both"></div> == Geschlossene Lautsprecherbox == [[Bild:Geschlossene.Lautsprecherbox.gif|upright=2|miniatur|rechts|Geschlossene Lautsprecherbox.]] Das ideale Verhalten eines Schalllabyrinths wird bei '''Lautsprecherboxen in geschlossener Bauweise''' nicht erreicht. Neben der nach vorne abgestrahlten Schallwelle <math>A_v</math> können auch die nach hinten abgestrahlten Schallwellen <math>A_h</math>, die an den Gehäuseinnenwänden reflektiert werden, durch die Lautsprecheröffnung hinaustreten. Als Folge der Reflexionen treten ferner stehende Wellen auf, die sich negativ auf den Amplituden- und Phasenfrequenzgang der Lautsprecherbox auswirken. Dies kann zwar eingeschränkt werden, indem das Innere der Lautsprecherbox mit einem schallschluckenden Medium gefüllt wird und die Gehäusewände nicht parallel ausgerichtet sind, jedoch lässt sich dieser Effekt nie vollständig vermeiden. Ferner kann besonders bei niedrigen Frequenzen ein so hoher Schalldruck auftreten, dass eine Rückkopplung auf die Lautsprechermembran stattfindet, die sich ebenso wie die reflektierten Schallwellen als nichtlinearer Effekt negativ bemerkbar macht. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich aus der Überlagerung aller Schallwellen: :<math>A_{ges} = A_v + B(A_h) + C(A_h)</math>, wobei die Funktion <math>B(A_h)</math> die im Lautsprechergehäuse gedämpften und reflektieren Schallwellen repräsentiert und <math>C(A_h)</math> die nichtlinearen Anteile durch Rückkopplung des Schalls. <div style="clear:both"></div> === Teilventilierte Lautsprecherbox === [[Bild:Teilventilierte.Lautsprecherbox.gif|upright=2|miniatur|rechts|Teilventilierte Lautsprecherbox.]] Der hohe Schalldruck wird bei den sogenannten '''teilventilierten Lautsprecherboxen''' durch eine kleine Öffnung vermieden, die zum Druckausgleich dient. Wenn solche Lautsprecherboxen gut bedämpft sind und keine parallelen Gehäusewände haben, damit sich keine stehenden Wellen aufbauen können, stellen sie einen recht guten Kompromiss dar, die nach hinten abgestrahlte Schallenergie findet aber auch hier keine Verwendung bei der Klangwiedergabe. <div style="clear:both"></div> === Bass-Reflex-Lautsprecherbox === [[Bild:Bass.Reflex.Lautsprecherbox.gif|upright=2|miniatur|rechts|Bass-Reflex-Lautsprecherbox.]] Bei '''Bass-Reflex-Lautsprecherboxen''' wird die Eigenfrequenz des Lautsprechergehäuses bewusst eingesetzt, um die Schallabstrahlung der Lautsprecherbox meist im Tieftonbereich zu verstärken. Die Lautsprecherbox hat eine zusätzliche, meist nach vorn gerichtete Rohröffnung und wirkt somit als Helmholtz-Resonator mit der Amplitudenfunktion <math>B_v(f,t)</math>. Es ist auf diese Weise jedoch problematisch, einen gleichmäßigen Frequenzgang zu erreichen, da in der Regel eine zu starke Verstärkung in einem zu schmalen Frequenzband besteht und dadurch die beiden nach vorne abgestrahlten Schallwellen vom Lautsprecher <math>A_v</math> und von der zusätzlichen Öffnung <math>B_v</math> sehr schlecht aufeinander abgestimmt werden können. Auch bei Bass-Reflex-Lautsprecherboxen kann es wie bei geschlossenen Lautsprecherboxen natürlich zusätzlich noch zu stehenden Wellen kommen. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich also im Wesentlichen aus der Überlagerung zweier Schallwellen: :<math>A_{ges} = A_v + B_v</math>, wobei die Funktion <math>A_v</math> die nach vorne abgestrahlte Schallwelle und <math>B_v</math> die aus der Bass-Reflex-Öffnung abgestrahlten Schallwellen repräsentiert. <div style="clear:both"></div> == Transmissionline-Lautsprecherbox == === Transmissionline ohne Dämpfung === [[Bild:Transmissionline.Lautsprecherbox.ohne.Daempfung.gif|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox ohne Dämpfung.]] Die vom Lautsprecher nach hinten abgegebene Schallwelle <math>A_h</math> kann durch einen Schallkanal mit der Länge <math>X_0</math> geleitet werden, an dessen Ende sich eine Öffnung befindet. Dieser idealerweise resonanzfreie Schallkanal wurde von dem an der Universität von Bradford lehrenden Briten Arthur R.&nbsp;Bailey '''Transmissionline''' genannt. Die aus dieser Öffnung heraustretende Schallwelle <math>A_{h,TL}</math> kann mit der vom Lautsprecher nach vorn abgestrahlten Schallwelle <math>A_v</math> interferieren. Die Amplitude der interferierten Schallwelle ergibt sich im Falle des ungedämpften Schallkanals aus der Summe der beiden Anteile: :<math>A_v = A_0(f)</math> wie beim freien Lautsprecher und :<math>A_{h,TL} = -A_0(f)\cdot e^{-i \omega \cdot {X_0 \over c_0}}</math> Hierbei ist <math>c_0</math> die Schallgeschwindigkeit im gasförmigen Schallmedium (in Luft bei 20°&nbsp;C und 60% relativer Luftfeuchtigkeit ist <math>c_0 = 344 {\frac {m} {s}}</math>). An der Lautsprecherfront ergibt sich für <math>A_{ges,TL}</math> ohne Dämpfung also: :<math>A_{ges,TL} = A_v + A_{h,TL} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {\frac {X_0} {c_0}}}\right)</math> <div style="clear:both"></div> === Transmissionline mit Dämpfung === ==== Qualitative Beschreibung ==== In den 1960er Jahren kam Bailey auf die Idee, die von einem Tieftöner nach hinten abgestrahlte Schallenergie in einem hinreichend langen, bedämpften und am Ende offenen Schallkanal quasi "totlaufen" zu lassen, was er in zwei Artikeln im Fachmagazin Wireless World beschrieben hat. Die Dämpfung ist dabei ein integraler Bestandteil der Konstruktion von Bailey. Die Dämpfung, die Schallgeschwindigkeit und somit auch die Wellenlänge der Schallwellen sind bei einem solchen Tiefpass frequenzabhängig (Dispersion). Die Schallwellen werden umso weniger stark gedämpft, je geringer die Frequenz beziehungsweise je größer die Wellenlänge der Schallwellen ist. Die höherfrequenten Anteile werden durch die akustische Dämpfung in Wärmeenergie umgewandelt, und die sehr tieffrequenten am Ende aus der Transmissionline noch austretenden Schallwellen können bei geeigneter Interferenz mit den direkten, nach vorne abgegebenen Schallwellen des eingesetzten Tieftöners den Amplitudenfrequenzgang bei tiefen Frequenzen verbessern. Die ersten nach diesem Prinzip gebauten kommerziellen Lautsprecherboxen wurden zunächst von der in Cambridgeshire ansässigen und mit Bailey kooperierenden Firma Radford und später unter anderem von IMF Electronics und in Deutschland von {{w|Lautsprecher Teufel}} auf den Markt gebracht. Die vom Lautsprecher nach hinten abgegebene Schallwelle wird in dem zum Beispiel mit langfaseriger Schafwolle gedämmten Schallkanal zunehmend abgeschwächt, sehr tiefe Frequenzen können den Schallkanal jedoch nur schwach gedämpft verlassen und zur Schallwiedergabe verwendet werden. Die Länge des Kanals sollte nach Bailey mindestens etwa ein Viertel der Wellenlänge der unteren Grenzfrequenz betragen, kann aber auch länger sein. Er kann in verschiedener Weise geknickt oder gefaltet werden, um in einem konventionellen Gehäuse Platz zu finden, jedoch sollten Reflexionen (zum Beispiel mit gekrümmten oder abgeschrägten Flächen) vermieden werden. Da bei diesem "nicht-resonanten Lautsprechergehäuse" (Bailey) ein großer Teil der nach hinten abgestrahlte Schallenergie vernichtet wird, ist der Wirkungsgrad von Transmissionline-Lautsprechern relativ schlecht, was jedoch dank leistungsfähiger (Transistor-)Verstärker normalerweise keine Rolle spielt. Bei korrekter Konstruktion zeichnen sich Transmissionline-Lautsprecher durch sehr gute, klangneutrale (Tief-)Basswiedergabe und sehr gutes Impulsverhalten aus. Wegen Größe, Gewicht und der relativ hohen Fertigungskosten konnten sie sich jedoch auf lange Sicht nicht auf dem Markt durchsetzen. <gallery caption="Tieffrequente Sinustöne mit 30 Sekunden Dauer" perrow="5"> Sine Wave 10Hz 30s.ogg|10 Hertz Sine Wave 20Hz 30s.ogg|20 Hertz Sine Wave 30Hz 30s.ogg|30 Hertz Sine Wave 40Hz 30s.ogg|40 Hertz Sine Wave 50Hz 30s.ogg|50 Hertz </gallery> ==== Quantitative Berechnung ==== [[Bild:Transmissionline.Lautsprecherbox.mit.Daempfung.png|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox mit Dämpfung.]] Im folgenden werden die von Bailey im Wesentlichen nur qualitativ beschriebenen Sachverhalte anhand entsprechender akustischer Formeln quantitativ beschrieben. Wird die Transmissionline mit einem porösen, faserigen Absorber bedämpft, so kann die aus der Transmissionline austretende Schallwellenamplitude <math>A_{h,TL,D}</math> nach Fridolin Peter Mechel mit der '''Theorie der quasihomogenen Absorber''' näherungsweise angegeben werden: :<math>A_{h,TL,D} = -A_0 (f)\cdot e^{-i \omega \cdot {X_0 \over c_0} \cdot \sqrt{1 - i \cdot {r \over {\rho_0 \cdot \omega}}}}</math> Dabei ist <math>\rho_0</math> die Dichte des gasförmigen Schallmediums (bei trockener Luft bei 20°&nbsp;C ist <math>\rho_0 = 1.2 {\text{kg} \over \text{m}^3}</math>) und <math>r</math> der spezifische Strömungswiderstand des porösen Absorbermaterials in Newtonsekunden pro Biquadratmeter. Je größer der spezifische Strömungswiderstand, desto stärker ist die Schalldämpfung. Er hängt im Wesentlichen vom Material, von der Faserfeinheit und von der Massendichte <math>\rho</math>, also von der Packungsdichte, des Absorbers und vom Füllgrad in der Transmissionline ab. Weiterhin hängt er von der gleichmäßigen Verteilung des Absorbers aber auch von Faserrichtung und der Schallfrequenz ab, so dass in der folgenden Tabelle nur ungefähre Richtwerte für einige poröse, faserige Absorber angeben sind: {| class="wikitable zebra" | style="text-align:center" !Material !Massendichte !Spezifischer<br/>Strömungswiderstand !Gewichtseffizienz |- | |<math>\rho</math> in <math>\frac {\text{kg}} {\text{m}^3}</math> |<math>r</math> in <math>\frac {\text{Ns}} {\text{m}^4}</math> |<math>\frac {r} {\rho}</math> in <math>\frac {\text{Ns}} {\text{kg m}} (= \text{Hz})</math> |- |Schafwolle |5 |4000 |800 |- |Schafwolle |10 |8000 |800 |- |Baumwolle |5 |1000 |200 |- |Baumwolle |10 |4000 |400 |- |Glaswolle |50 |200 |4 |- |Glasfaserplatten |20 |1000 |50 |- |Mineralwolle |10 |600 |60 |- |Mineralwolle |50 |10000 |200 |- |Aluminiumwolle |35 |500 |14,3 |- |Aluminiumwolle |70 |4000 |57,1 |} [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Laengen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Längen.]] [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Daempfungen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Dämpfungen.]] Für die gesamte Transmissionline-Lautsprecherbox ergibt sich an der Front der Lautsprecherbox mit Dämpfung die folgende frequenzabhängige Amplitude: :<math>A_{ges,TL,D} = A_v + A_{h,TL,D} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {X_0 \over c_0} \cdot \sqrt{1 - i \cdot {r \over {\rho_0 \cdot \omega}}}}\right)</math> Der Amplitudenverlauf ergibt sich aus dem Betrag der komplexen Funktion <math>A_{ges,TL,D}</math>. Dieser muss entsprechend den Parametern Gesamtgüte des Tieftöners <math>Q</math>, Länge der Transmissionline <math>X_0</math> und spezifischer Strömungswiderstand <math>r</math> so angepasst werden, dass bei den tiefen Frequenzen ein möglichst gleichmäßiger Amplitudenverlauf entsteht. Die beiden nebenstehenden Abbildungen sollen den Einfluss der beiden Parameter <math>X_0</math> und <math>r</math> bei vorgegebenem <math>Q</math> verdeutlichen. Die als geeignet bestimmten Parameter können beim Bau einer Transmissionline-Lautsprecherbox umgesetzt werden. Insbesondere die richtige Bedämpfung muss hierbei jedoch experimentell überprüft und gegebenenfalls korrigiert werden. Ferner muss bei der Ansteuerung von Tief- und Mitteltöner die aus obiger Gleichung resultierende Amplitude :<math>|A_{ges,TL,D}|</math> und Phasendifferenz :<math>arg(A_{ges,TL,D})</math> bei der Übergangsfrequenz zwischen dem System aus Transmissionline und Tieftöner auf der einen Seite und Mitteltöner auf der anderen Seite mittels der Frequenzweiche ausgeglichen werden. <div style="clear:both"></div> === Erfahrungswerte für den Entwurf einer Transmissionline-Lautsprecherbox === [[Datei:Transmissionline-Lautsprecher.P1117437.jpg|mini|hochkant=1.5|Transmissionline-Lautsprecherbox aus dem Jahr 1986 mit trapezförmiger Grundfläche, oben der Tieftöner, darunter der Mitteltöner und unten der Hochtöner. Die beiden Öffnungen der unten im Gehäuse symmetrisch nach rechts und links aufgeteilten akustischen Transmissionlines befinden sich oben an den abgeschrägten Seiten hinter dem Tieftöner. Rechnerisch wird im Frequenzgang beim Unterschreiten von 30&nbsp;Hertz ein Pegelabfall von drei Dezibel erreicht. In einem größeren Abhöraum betrug die Pegelschwankung der Kombination aus Tieftöner und Transmissionline zwischen 20&nbsp;und 50&nbsp;Hertz ungefähr ein Dezibel.]] Der Querschnitt der Transmissionline direkt hinter dem Lautsprecher sollte mindestens so groß sein wie die Lautsprechermembranfläche oder sogar etwas darüber liegen. Entlang der Transmissionline kann sich deren Querschnitt um maximal 25&nbsp;Prozent verringern. Für den Transmissionline-Betrieb geeignete Tieftöner sollten eine relativ hohe Gesamtgüte <math>\left(Q \lessapprox 1\right)</math> haben. Mit einer passiven Frequenzweiche, über aktive Frequenzfilter beziehungsweise mit Hilfe von digitalen Signalprozessoren werden die elektrischen Signale auf die Tief- und Mittel- sowie gegebenenfalls auch die Hochtöner verteilt. Um unerwünschte Effekte durch akustische Interferenzen zu minimieren, sollten die seitlichen Abstände zwischen Mittel- und Tieftöner sowie zwischen Hoch- und Mitteltöner so gering wie möglich gehalten werden. Außerdem sollten alle Lautsprecher bündig mit dem Gehäuse abschließen und in einer Ebene liegen. Die Austrittsöffnung einer Transmissionline kann konstruktiv wie ein Subtieftöner betrachtet werden. Der Wert des spezifischen Strömungswiderstands <math>r</math> (und somit der Dichte <math>\rho_0</math>) sollte bei der fertiggestellten Lautsprecherbox experimentell überprüft (zum Beispiel mit Frequenzgenerator oder mit Rauschgenerator und Frequenzanalysator) und gegebenenfalls angepasst werden, da er für einen in die Transmissionline eingebauten Absorber meist nicht genau genug berechnet werden kann. Der Mess- oder Abhörraum hat durch seine Eigenfrequenzen im tiefen Frequenzbereich einen deutlichen Einfluss auf die Tieftonwiedergabe. Insbesondere bei parallelen Wänden (respektive Böden und Decken) können stehende Wellen Raumresonanzen hervorrufen, wenn die Schallwellenlänge <math>\lambda</math> dem Doppelten einer Raumdimension mit der Länge <math>s</math> entspricht: :<math>f_{Resonanz} = \frac {c_0} {\lambda} = \frac {c_0} {2 \cdot s}</math> In einem quaderförmigen Raum mit der Länge <math>l</math>, der Breite <math>b</math> und der Höhe <math>h</math> ergibt sich nach dem Satz der Pythagoras die kleinste Resonanzfrequenz aus der Raumdiagonale <math>d</math>: :<math>d = \sqrt {l^2 + b^2 + h^2}</math> :<math>f_{Resonanz, min} = \frac {c_0} {2 \cdot d} = \frac {c_0} {2 \cdot \sqrt {l^2 + b^2 + h^2}}</math> Ein quaderförmiger Raum mit einer quadratischen Grundfläche von 100&nbsp;Quadratmetern hat demnach eine niedrigste Eigenfrequenz von ungefähr 12&nbsp;Hertz. Durch schwere Vorhänge und Teppiche oder andere Schallabsorber kann die Schallreflexion in einem Raum deutlich reduziert werden, wobei die Schallabsorption für tiefer werdende Frequenzen in der Regel zunehmend geringer wird. Im freien Feld beziehungsweise in schallreflexionsarmen Messräumen entfallen solche Raumreflexionen oder sind so stark reduziert, so dass sich für einen gegebenen Lautsprecher ein anderes Klangbild als in einem gewöhnlichen Raum ergibt. {| class="wikitable" |+ Parameter der abgebildeten Transmissionline-Lautsprecherbox ! Parameter !! Formelsymbol !! Wert |- | Eigenresonanzfrequenz des Tieftöners || <math>f_0</math> || <math>\text {31 Hertz}</math> |- | Güte des Tieftöners || <math>Q</math> || <math>\text {0,65}</math> |- | Länge der Transmissionline || <math>X_0</math> || <math>\text {2,75 Meter}</math> |- | Mittlerer spezifischer Widerstand der Dämmung aus Mineralwolle || <math>r</math> || <math>\text {1500 } \frac {\text {Newtonsekunden}} {\text {Biquadratmeter}}</math> |} ==Widmung== Diese Zusammenstellung ist meinem Studienfreund '''Dr.&nbsp;Lutz König''' gewidmet. Der Hauptautor dankt ihm für seine freundschaftlichen, umfangreichen und stets förderlichen Beiträge zur Realisierung unserer 1986 in Eigenentwicklung hergestellten vier Lautsprecherboxen. == Literatur == * Arthur R.&nbsp;Bailey: ''A Non-resonant Loudspeaker Enclosure Design'' - ''Using acoustic transmission line with low-pass filter characteristics'', Wireless World, October 1965, p.&nbsp;483-486 * Arthur R.&nbsp;Bailey: ''The Transmission-line Loudspeaker Enclosure'' - ''A re-examination of the general principle and a suggested new method of construction'', Wireless World, May 1972, p.&nbsp;215-217 * {{w|Ludwig Bergmann (Physiker)|Ludwig Bergmann}}, {{w|Clemens Schaefer (Physiker)|Clemens Schäfer}}: ''{{w|Bergmann-Schaefer_Lehrbuch_der_Experimentalphysik|Lehrbuch der Experimentalphysik}}'', Band 1: ''Mechanik, Akustik, Wärme'', 9. Auflage, de Gruyter, 1974, ISBN 978-3-1100-4861-2 * Fridolin P.&nbsp;Mechel: ''Schallabsorption'', Kapitel&nbsp;18, in: {{w|Manfred Heckl}}, {{w|Helmut A. Müller (Akustiker)|Helmut A.&nbsp;Müller}}: ''Taschenbuch der technischen Akustik'', Springer-Verlag, Berlin, Heidelberg, New York, 1975, ISBN 3-6429-7357-4 * Hans Herbert Klinger: ''Lautsprecher und Lautsprechergehäuse für HiFi'', Franzis-Verlag GmbH, München, 1981, ISBN 3-7723-1051-6 * Heinz Sahm: ''HiFi-Lautsprecher'', ''Grundlagen der elektrodynamischen Lautsprecher in unendlicher Schallwand und im Gehäuse'', Franzis-Verlag GmbH, München, 1982, ISBN 3-7723-6522-1 * Berndt Stark: ''Lautsprecher-Handbuch'' - ''Theorie und Praxis des Boxenbauens'', Richard Pflaum Verlag, München, 1985, ISBN 3-7905-0433-5 == Siehe auch == * [[Grundlegendes zur Akustik]] == Weblinks == {{Commonscat|Transmission line loudspeakers}} == Zusammenfassung des Projekts == {{Vorlage:StatusBuch|10}} * '''Zielgruppe und Lernziele:''' Dieses Buch richtet sich an akustisch interessierte Leser mit mathematischer Ausbildung. Es beschreibt einen vollständigen Weg zur Berechnung einer bedämpften akustischen Transmissionline mit komplexwertigen Funktionen. Aufbauend auf grundlegenden Betrachtungen zur Schallerzeugung und -abstrahlung von Lautsprechern wird durch schrittweise Erweiterungen und Ergänzungen die Funktionsweise einer solchen Transmissionline erarbeitet. * '''Ansprechperson:''' Ansprechpartner ist [[Benutzer:Bautsch]] [[Kategorie:Geometrische Kuriositäten‎]] 5g5ewjzqkp7z6l14ul9qyl5jdmuinlf 1088121 1088120 2026-06-13T23:02:18Z Bautsch 35687 /* Quantitative Berechnung */ \eta 1088121 wikitext text/x-wiki {{Regal | ort=Physik}} {{Infoleiste|Physik|Studium|S|Technik}} [[Bild:Transmission-line.png|rechts|mini|Beispiel für eine Transmissionline als akustischen Kanal (blaue Pfeile) für den rückwärtigen Schall eines Lautsprechers (orangefarben) in einer Lautsprecherbox (dunkelbraun).]] == Einführung == '''Transmissionline''' ("Übertragungsleitung") nennt man in der Lautsprecherakustik einen in der Regel mehrfach gefalteten Kanal, durch den der von einem Lautsprecher nach hinten abgegebene Schall hindurchtritt. Am Ende dieses Kanals befindet sich eine Öffnung, durch die der Schall heraustreten kann. Dieser Schall kann unter geeigneten Voraussetzungen dazu verwendet werden, den Amplituden-Frequenzgang einer elektrodynamischen Lautsprecherbox am tieffrequenten Ende zu erweitern und zu verbessern. Ausgehend von idealisierten theoretischen Ansätzen zum Abstrahlverhalten von Lautsprechern und mit der Theorie der quasihomogenen Absorber wird in diesem Buch darauf eingegangen, wie der Tieftonbereich einer Transmissionline-Lautsprecherbox dimensioniert und akustisch gedämpft werden kann. Dazu werden wegen des einfachen mathematischen Formalismus Funktionen komplexer Zahlen ({{w|Eulersche Formel}}) herangezogen. Dieses Wikibook basiert auf der privaten Vorveröffentlichung ''Anleitung zur akustischen Berechnung einer elektrodynamischen "transmission line"-Lautsprecherbox'' aus dem Jahr 2000. Das hier vorgestellte Berechnungsverfahren wurde vom Autor im Jahr 1986 entwickelt und für den Bau von Transmissionline-Lautsprecherboxen angewandt. <div style="clear:both"></div> == Abstrahlverhalten von Lautsprechern == [[Bild:Amplitudenfrequenzgang.Lautsprecher.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang eines elektrodynamischen Lautsprechers.]] Der Amplitudenfrequenzgang <math>A_0</math> eines elektrodynamischen Lautsprechers mit der Gesamtgüte <math>Q</math> und der Eigenfrequenz <math>f_0</math> berechnet sich über der Schallfrequenz <math>f</math> theoretisch zu :<math>A_0 (f) = \frac {\frac {f} {f_0}} {\sqrt {\frac {1} {Q^2} + \left(\frac {f} {f_0} - \frac {f_0} {f}\right)^2}}</math> Im Resonanzfall <math>f = f_0</math> vereinfacht sich diese Gleichung zu :<math>A_0 (f_0) = Q</math> Mit zunehmender Frequenz fällt der Frequenzgang jedoch entgegen der Aussage obiger Gleichung wieder ab, da die Lautsprechermembran wegen ihrer mechanischen Trägheit nicht mehr mitschwingen kann. Weitere Abweichungen ergeben sich in der Praxis, weil auch andere Eigenfrequenzen als <math>f_0</math> noch kleine Beiträge zum Frequenzgang leisten. Diese Eigenfrequenzen werden im Allgemeinen durch ungewollte Schwingungsmoden der Lautsprechermembran verursacht. <div style="clear:both"></div> === Freier Lautsprecher === [[Bild:Freier.Lautsprecher.gif|upright=2|miniatur|rechts|Freier Lautsprecher.]] Wird ein Lautsprecher bei der Frequenz <math>f</math> ohne Schallwand oder Gehäuse betrieben, so interferieren an der Stelle <math>x = 0</math> (also an der Lautsprecheroberfläche) über der Zeit <math>t</math> die nach vorne abgegebene Schallwelle :<math>A_v = A(f,t) = A_0(f) \cdot e^{-i \omega t}</math> und die nach hinten abgestrahlte Schallwelle :<math>A_h = -A(f,t) = -A_0(f) \cdot e^{-i \omega t}</math> mit der {{w|Eulersche Zahl|Eulerschen Zahl}} <math>e</math>, der {{w|Imaginäre Zahl|imaginären Einheit}} <math>i = \sqrt {-1}</math> und der {{w|Kreisfrequenz}} <math>\omega = 2 \pi f</math> zu allen Zeitpunkten destruktiv: :<math>A_{ges} = A_v + A_h = 0</math> Dies bedeutet, dass die gesamte Amplitudenfrequenzgang <math>A_{ges}</math> null wird, da sich die beiden Schallwellen gegenseitig auslöschen. Dies wird auch als {{w|Akustischer Kurzschluss|akustischer Kurzschluss}} bezeichnet. <div style="clear:both"></div> === Lautsprecher mit Schallwand === [[Bild:Lautsprecher.mit.Schallwand.gif|upright=2|miniatur|rechts|Lautsprecher mit unendlich ausgedehnter Schallwand.]] Die Probleme beim freien Lautsprecher können umgangen werden, wenn der Lautsprecher in eine im Verhältnis zur größten abzustrahlenden Schallwellenlänge möglichst weit ausgedehnte Schallwand eingebaut wird. Allerdings muss eine solche Schallwand meist sehr große Abmessungen haben. Bei einer minimalen Frequenz von beispielsweise 30&nbsp;Hertz müsste die Schallwand seitlich mehrere Meter Ausdehnung haben. :<math>A_{ges} = A_v = A(f,t) = A_0(f)\cdot e^{-i \omega t}</math> wobei <math>A_v</math> die nach vorne abgestrahlte Schallwelle repräsentiert, die in diesem Fall mit der gesamten abgestrahlten <math>A_{ges}</math> identisch ist. <div style="clear:both"></div> === Schallabyrinth === [[Bild: Schallabyrinth.gif|upright=2|miniatur|rechts|Schalllabyrinth.]] Eine Alternative wäre der Einbau des Lautsprechers in ein sogenanntes '''Schallabyrinth'''. Die Schallenergie der nach hinten abgegebenen Schallwelle wird hier durch Mehrfachreflexion und Mehrfachstreuung in Verbindung mit Absorption vernichtet. Auch ein solches Schallabyrinth muss jedoch sehr groß dimensioniert werden, damit der Schall weitgehend absorbiert werden kann. Wie auch bei der Schallwand besteht der Nachteil, dass die vom Lautsprecher nach hinten abgestrahlte Schallenergie nicht genutzt wird. <div style="clear:both"></div> == Geschlossene Lautsprecherbox == [[Bild:Geschlossene.Lautsprecherbox.gif|upright=2|miniatur|rechts|Geschlossene Lautsprecherbox.]] Das ideale Verhalten eines Schalllabyrinths wird bei '''Lautsprecherboxen in geschlossener Bauweise''' nicht erreicht. Neben der nach vorne abgestrahlten Schallwelle <math>A_v</math> können auch die nach hinten abgestrahlten Schallwellen <math>A_h</math>, die an den Gehäuseinnenwänden reflektiert werden, durch die Lautsprecheröffnung hinaustreten. Als Folge der Reflexionen treten ferner stehende Wellen auf, die sich negativ auf den Amplituden- und Phasenfrequenzgang der Lautsprecherbox auswirken. Dies kann zwar eingeschränkt werden, indem das Innere der Lautsprecherbox mit einem schallschluckenden Medium gefüllt wird und die Gehäusewände nicht parallel ausgerichtet sind, jedoch lässt sich dieser Effekt nie vollständig vermeiden. Ferner kann besonders bei niedrigen Frequenzen ein so hoher Schalldruck auftreten, dass eine Rückkopplung auf die Lautsprechermembran stattfindet, die sich ebenso wie die reflektierten Schallwellen als nichtlinearer Effekt negativ bemerkbar macht. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich aus der Überlagerung aller Schallwellen: :<math>A_{ges} = A_v + B(A_h) + C(A_h)</math>, wobei die Funktion <math>B(A_h)</math> die im Lautsprechergehäuse gedämpften und reflektieren Schallwellen repräsentiert und <math>C(A_h)</math> die nichtlinearen Anteile durch Rückkopplung des Schalls. <div style="clear:both"></div> === Teilventilierte Lautsprecherbox === [[Bild:Teilventilierte.Lautsprecherbox.gif|upright=2|miniatur|rechts|Teilventilierte Lautsprecherbox.]] Der hohe Schalldruck wird bei den sogenannten '''teilventilierten Lautsprecherboxen''' durch eine kleine Öffnung vermieden, die zum Druckausgleich dient. Wenn solche Lautsprecherboxen gut bedämpft sind und keine parallelen Gehäusewände haben, damit sich keine stehenden Wellen aufbauen können, stellen sie einen recht guten Kompromiss dar, die nach hinten abgestrahlte Schallenergie findet aber auch hier keine Verwendung bei der Klangwiedergabe. <div style="clear:both"></div> === Bass-Reflex-Lautsprecherbox === [[Bild:Bass.Reflex.Lautsprecherbox.gif|upright=2|miniatur|rechts|Bass-Reflex-Lautsprecherbox.]] Bei '''Bass-Reflex-Lautsprecherboxen''' wird die Eigenfrequenz des Lautsprechergehäuses bewusst eingesetzt, um die Schallabstrahlung der Lautsprecherbox meist im Tieftonbereich zu verstärken. Die Lautsprecherbox hat eine zusätzliche, meist nach vorn gerichtete Rohröffnung und wirkt somit als Helmholtz-Resonator mit der Amplitudenfunktion <math>B_v(f,t)</math>. Es ist auf diese Weise jedoch problematisch, einen gleichmäßigen Frequenzgang zu erreichen, da in der Regel eine zu starke Verstärkung in einem zu schmalen Frequenzband besteht und dadurch die beiden nach vorne abgestrahlten Schallwellen vom Lautsprecher <math>A_v</math> und von der zusätzlichen Öffnung <math>B_v</math> sehr schlecht aufeinander abgestimmt werden können. Auch bei Bass-Reflex-Lautsprecherboxen kann es wie bei geschlossenen Lautsprecherboxen natürlich zusätzlich noch zu stehenden Wellen kommen. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich also im Wesentlichen aus der Überlagerung zweier Schallwellen: :<math>A_{ges} = A_v + B_v</math>, wobei die Funktion <math>A_v</math> die nach vorne abgestrahlte Schallwelle und <math>B_v</math> die aus der Bass-Reflex-Öffnung abgestrahlten Schallwellen repräsentiert. <div style="clear:both"></div> == Transmissionline-Lautsprecherbox == === Transmissionline ohne Dämpfung === [[Bild:Transmissionline.Lautsprecherbox.ohne.Daempfung.gif|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox ohne Dämpfung.]] Die vom Lautsprecher nach hinten abgegebene Schallwelle <math>A_h</math> kann durch einen Schallkanal mit der Länge <math>X_0</math> geleitet werden, an dessen Ende sich eine Öffnung befindet. Dieser idealerweise resonanzfreie Schallkanal wurde von dem an der Universität von Bradford lehrenden Briten Arthur R.&nbsp;Bailey '''Transmissionline''' genannt. Die aus dieser Öffnung heraustretende Schallwelle <math>A_{h,TL}</math> kann mit der vom Lautsprecher nach vorn abgestrahlten Schallwelle <math>A_v</math> interferieren. Die Amplitude der interferierten Schallwelle ergibt sich im Falle des ungedämpften Schallkanals aus der Summe der beiden Anteile: :<math>A_v = A_0(f)</math> wie beim freien Lautsprecher und :<math>A_{h,TL} = -A_0(f)\cdot e^{-i \omega \cdot {X_0 \over c_0}}</math> Hierbei ist <math>c_0</math> die Schallgeschwindigkeit im gasförmigen Schallmedium (in Luft bei 20°&nbsp;C und 60% relativer Luftfeuchtigkeit ist <math>c_0 = 344 {\frac {m} {s}}</math>). An der Lautsprecherfront ergibt sich für <math>A_{ges,TL}</math> ohne Dämpfung also: :<math>A_{ges,TL} = A_v + A_{h,TL} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {\frac {X_0} {c_0}}}\right)</math> <div style="clear:both"></div> === Transmissionline mit Dämpfung === ==== Qualitative Beschreibung ==== In den 1960er Jahren kam Bailey auf die Idee, die von einem Tieftöner nach hinten abgestrahlte Schallenergie in einem hinreichend langen, bedämpften und am Ende offenen Schallkanal quasi "totlaufen" zu lassen, was er in zwei Artikeln im Fachmagazin Wireless World beschrieben hat. Die Dämpfung ist dabei ein integraler Bestandteil der Konstruktion von Bailey. Die Dämpfung, die Schallgeschwindigkeit und somit auch die Wellenlänge der Schallwellen sind bei einem solchen Tiefpass frequenzabhängig (Dispersion). Die Schallwellen werden umso weniger stark gedämpft, je geringer die Frequenz beziehungsweise je größer die Wellenlänge der Schallwellen ist. Die höherfrequenten Anteile werden durch die akustische Dämpfung in Wärmeenergie umgewandelt, und die sehr tieffrequenten am Ende aus der Transmissionline noch austretenden Schallwellen können bei geeigneter Interferenz mit den direkten, nach vorne abgegebenen Schallwellen des eingesetzten Tieftöners den Amplitudenfrequenzgang bei tiefen Frequenzen verbessern. Die ersten nach diesem Prinzip gebauten kommerziellen Lautsprecherboxen wurden zunächst von der in Cambridgeshire ansässigen und mit Bailey kooperierenden Firma Radford und später unter anderem von IMF Electronics und in Deutschland von {{w|Lautsprecher Teufel}} auf den Markt gebracht. Die vom Lautsprecher nach hinten abgegebene Schallwelle wird in dem zum Beispiel mit langfaseriger Schafwolle gedämmten Schallkanal zunehmend abgeschwächt, sehr tiefe Frequenzen können den Schallkanal jedoch nur schwach gedämpft verlassen und zur Schallwiedergabe verwendet werden. Die Länge des Kanals sollte nach Bailey mindestens etwa ein Viertel der Wellenlänge der unteren Grenzfrequenz betragen, kann aber auch länger sein. Er kann in verschiedener Weise geknickt oder gefaltet werden, um in einem konventionellen Gehäuse Platz zu finden, jedoch sollten Reflexionen (zum Beispiel mit gekrümmten oder abgeschrägten Flächen) vermieden werden. Da bei diesem "nicht-resonanten Lautsprechergehäuse" (Bailey) ein großer Teil der nach hinten abgestrahlte Schallenergie vernichtet wird, ist der Wirkungsgrad von Transmissionline-Lautsprechern relativ schlecht, was jedoch dank leistungsfähiger (Transistor-)Verstärker normalerweise keine Rolle spielt. Bei korrekter Konstruktion zeichnen sich Transmissionline-Lautsprecher durch sehr gute, klangneutrale (Tief-)Basswiedergabe und sehr gutes Impulsverhalten aus. Wegen Größe, Gewicht und der relativ hohen Fertigungskosten konnten sie sich jedoch auf lange Sicht nicht auf dem Markt durchsetzen. <gallery caption="Tieffrequente Sinustöne mit 30 Sekunden Dauer" perrow="5"> Sine Wave 10Hz 30s.ogg|10 Hertz Sine Wave 20Hz 30s.ogg|20 Hertz Sine Wave 30Hz 30s.ogg|30 Hertz Sine Wave 40Hz 30s.ogg|40 Hertz Sine Wave 50Hz 30s.ogg|50 Hertz </gallery> ==== Quantitative Berechnung ==== [[Bild:Transmissionline.Lautsprecherbox.mit.Daempfung.png|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox mit Dämpfung.]] Im folgenden werden die von Bailey im Wesentlichen nur qualitativ beschriebenen Sachverhalte anhand entsprechender akustischer Formeln quantitativ beschrieben. Wird die Transmissionline mit einem porösen, faserigen Absorber bedämpft, so kann die aus der Transmissionline austretende Schallwellenamplitude <math>A_{h,TL,D}</math> nach Fridolin Peter Mechel mit der '''Theorie der quasihomogenen Absorber''' näherungsweise angegeben werden: :<math>A_{h,TL,D} = -A_0 (f)\cdot e^{-i \omega \cdot {X_0 \over c_0} \cdot \sqrt{1 - i \cdot {r \over {\rho_0 \cdot \omega}}}}</math> Dabei ist <math>\rho_0</math> die Dichte des gasförmigen Schallmediums (bei trockener Luft bei 20°&nbsp;C ist <math>\rho_0 = 1.2 {\text{kg} \over \text{m}^3}</math>) und <math>r</math> der spezifische Strömungswiderstand des porösen Absorbermaterials in Newtonsekunden pro Biquadratmeter. Je größer der spezifische Strömungswiderstand, desto stärker ist die Schalldämpfung. Er hängt im Wesentlichen vom Material, von der Faserfeinheit und von der Massendichte <math>\rho</math>, also von der Packungsdichte, des Absorbers und vom Füllgrad in der Transmissionline ab. Weiterhin hängt er von der gleichmäßigen Verteilung des Absorbers aber auch von Faserrichtung und der Schallfrequenz ab, so dass in der folgenden Tabelle nur ungefähre Richtwerte für einige poröse, faserige Absorber angeben sind: {| class="wikitable zebra" | style="text-align:center" !Material !Massendichte !Spezifischer<br/>Strömungswiderstand !Gewichtseffizienz |- | |<math>\rho</math> in <math>\frac {\text{kg}} {\text{m}^3}</math> |<math>r</math> in <math>\frac {\text{Ns}} {\text{m}^4}</math> |<math>\eta = \frac {r} {\rho}</math> in <math>\frac {\text{Ns}} {\text{kg m}} = \text{Hz}</math> |- |Schafwolle |5 |4000 |800 |- |Schafwolle |10 |8000 |800 |- |Baumwolle |5 |1000 |200 |- |Baumwolle |10 |4000 |400 |- |Glaswolle |50 |200 |4 |- |Glasfaserplatten |20 |1000 |50 |- |Mineralwolle |10 |600 |60 |- |Mineralwolle |50 |10000 |200 |- |Aluminiumwolle |35 |500 |14,3 |- |Aluminiumwolle |70 |4000 |57,1 |} Die Gewichtseffizienz des Absorbermaterials <math>\eta</math> ist in der Tabelle als Verhältnis der spezifischen Strömungswiderstands <math>r</math> zur Massendichte <math>\rho</math> angegeben: :<math>\eta = \frac {r} {\rho}</math> [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Laengen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Längen.]] [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Daempfungen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Dämpfungen.]] Für die gesamte Transmissionline-Lautsprecherbox ergibt sich an der Front der Lautsprecherbox mit Dämpfung die folgende frequenzabhängige Amplitude: :<math>A_{ges,TL,D} = A_v + A_{h,TL,D} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {X_0 \over c_0} \cdot \sqrt{1 - i \cdot {r \over {\rho_0 \cdot \omega}}}}\right)</math> Der Amplitudenverlauf ergibt sich aus dem Betrag der komplexen Funktion <math>A_{ges,TL,D}</math>. Dieser muss entsprechend den Parametern Gesamtgüte des Tieftöners <math>Q</math>, Länge der Transmissionline <math>X_0</math> und spezifischer Strömungswiderstand <math>r</math> so angepasst werden, dass bei den tiefen Frequenzen ein möglichst gleichmäßiger Amplitudenverlauf entsteht. Die beiden nebenstehenden Abbildungen sollen den Einfluss der beiden Parameter <math>X_0</math> und <math>r</math> bei vorgegebenem <math>Q</math> verdeutlichen. Die als geeignet bestimmten Parameter können beim Bau einer Transmissionline-Lautsprecherbox umgesetzt werden. Insbesondere die richtige Bedämpfung muss hierbei jedoch experimentell überprüft und gegebenenfalls korrigiert werden. Ferner muss bei der Ansteuerung von Tief- und Mitteltöner die aus obiger Gleichung resultierende Amplitude :<math>|A_{ges,TL,D}|</math> und Phasendifferenz :<math>arg(A_{ges,TL,D})</math> bei der Übergangsfrequenz zwischen dem System aus Transmissionline und Tieftöner auf der einen Seite und Mitteltöner auf der anderen Seite mittels der Frequenzweiche ausgeglichen werden. <div style="clear:both"></div> === Erfahrungswerte für den Entwurf einer Transmissionline-Lautsprecherbox === [[Datei:Transmissionline-Lautsprecher.P1117437.jpg|mini|hochkant=1.5|Transmissionline-Lautsprecherbox aus dem Jahr 1986 mit trapezförmiger Grundfläche, oben der Tieftöner, darunter der Mitteltöner und unten der Hochtöner. Die beiden Öffnungen der unten im Gehäuse symmetrisch nach rechts und links aufgeteilten akustischen Transmissionlines befinden sich oben an den abgeschrägten Seiten hinter dem Tieftöner. Rechnerisch wird im Frequenzgang beim Unterschreiten von 30&nbsp;Hertz ein Pegelabfall von drei Dezibel erreicht. In einem größeren Abhöraum betrug die Pegelschwankung der Kombination aus Tieftöner und Transmissionline zwischen 20&nbsp;und 50&nbsp;Hertz ungefähr ein Dezibel.]] Der Querschnitt der Transmissionline direkt hinter dem Lautsprecher sollte mindestens so groß sein wie die Lautsprechermembranfläche oder sogar etwas darüber liegen. Entlang der Transmissionline kann sich deren Querschnitt um maximal 25&nbsp;Prozent verringern. Für den Transmissionline-Betrieb geeignete Tieftöner sollten eine relativ hohe Gesamtgüte <math>\left(Q \lessapprox 1\right)</math> haben. Mit einer passiven Frequenzweiche, über aktive Frequenzfilter beziehungsweise mit Hilfe von digitalen Signalprozessoren werden die elektrischen Signale auf die Tief- und Mittel- sowie gegebenenfalls auch die Hochtöner verteilt. Um unerwünschte Effekte durch akustische Interferenzen zu minimieren, sollten die seitlichen Abstände zwischen Mittel- und Tieftöner sowie zwischen Hoch- und Mitteltöner so gering wie möglich gehalten werden. Außerdem sollten alle Lautsprecher bündig mit dem Gehäuse abschließen und in einer Ebene liegen. Die Austrittsöffnung einer Transmissionline kann konstruktiv wie ein Subtieftöner betrachtet werden. Der Wert des spezifischen Strömungswiderstands <math>r</math> (und somit der Dichte <math>\rho_0</math>) sollte bei der fertiggestellten Lautsprecherbox experimentell überprüft (zum Beispiel mit Frequenzgenerator oder mit Rauschgenerator und Frequenzanalysator) und gegebenenfalls angepasst werden, da er für einen in die Transmissionline eingebauten Absorber meist nicht genau genug berechnet werden kann. Der Mess- oder Abhörraum hat durch seine Eigenfrequenzen im tiefen Frequenzbereich einen deutlichen Einfluss auf die Tieftonwiedergabe. Insbesondere bei parallelen Wänden (respektive Böden und Decken) können stehende Wellen Raumresonanzen hervorrufen, wenn die Schallwellenlänge <math>\lambda</math> dem Doppelten einer Raumdimension mit der Länge <math>s</math> entspricht: :<math>f_{Resonanz} = \frac {c_0} {\lambda} = \frac {c_0} {2 \cdot s}</math> In einem quaderförmigen Raum mit der Länge <math>l</math>, der Breite <math>b</math> und der Höhe <math>h</math> ergibt sich nach dem Satz der Pythagoras die kleinste Resonanzfrequenz aus der Raumdiagonale <math>d</math>: :<math>d = \sqrt {l^2 + b^2 + h^2}</math> :<math>f_{Resonanz, min} = \frac {c_0} {2 \cdot d} = \frac {c_0} {2 \cdot \sqrt {l^2 + b^2 + h^2}}</math> Ein quaderförmiger Raum mit einer quadratischen Grundfläche von 100&nbsp;Quadratmetern hat demnach eine niedrigste Eigenfrequenz von ungefähr 12&nbsp;Hertz. Durch schwere Vorhänge und Teppiche oder andere Schallabsorber kann die Schallreflexion in einem Raum deutlich reduziert werden, wobei die Schallabsorption für tiefer werdende Frequenzen in der Regel zunehmend geringer wird. Im freien Feld beziehungsweise in schallreflexionsarmen Messräumen entfallen solche Raumreflexionen oder sind so stark reduziert, so dass sich für einen gegebenen Lautsprecher ein anderes Klangbild als in einem gewöhnlichen Raum ergibt. {| class="wikitable" |+ Parameter der abgebildeten Transmissionline-Lautsprecherbox ! Parameter !! Formelsymbol !! Wert |- | Eigenresonanzfrequenz des Tieftöners || <math>f_0</math> || <math>\text {31 Hertz}</math> |- | Güte des Tieftöners || <math>Q</math> || <math>\text {0,65}</math> |- | Länge der Transmissionline || <math>X_0</math> || <math>\text {2,75 Meter}</math> |- | Mittlerer spezifischer Widerstand der Dämmung aus Mineralwolle || <math>r</math> || <math>\text {1500 } \frac {\text {Newtonsekunden}} {\text {Biquadratmeter}}</math> |} ==Widmung== Diese Zusammenstellung ist meinem Studienfreund '''Dr.&nbsp;Lutz König''' gewidmet. Der Hauptautor dankt ihm für seine freundschaftlichen, umfangreichen und stets förderlichen Beiträge zur Realisierung unserer 1986 in Eigenentwicklung hergestellten vier Lautsprecherboxen. == Literatur == * Arthur R.&nbsp;Bailey: ''A Non-resonant Loudspeaker Enclosure Design'' - ''Using acoustic transmission line with low-pass filter characteristics'', Wireless World, October 1965, p.&nbsp;483-486 * Arthur R.&nbsp;Bailey: ''The Transmission-line Loudspeaker Enclosure'' - ''A re-examination of the general principle and a suggested new method of construction'', Wireless World, May 1972, p.&nbsp;215-217 * {{w|Ludwig Bergmann (Physiker)|Ludwig Bergmann}}, {{w|Clemens Schaefer (Physiker)|Clemens Schäfer}}: ''{{w|Bergmann-Schaefer_Lehrbuch_der_Experimentalphysik|Lehrbuch der Experimentalphysik}}'', Band 1: ''Mechanik, Akustik, Wärme'', 9. Auflage, de Gruyter, 1974, ISBN 978-3-1100-4861-2 * Fridolin P.&nbsp;Mechel: ''Schallabsorption'', Kapitel&nbsp;18, in: {{w|Manfred Heckl}}, {{w|Helmut A. Müller (Akustiker)|Helmut A.&nbsp;Müller}}: ''Taschenbuch der technischen Akustik'', Springer-Verlag, Berlin, Heidelberg, New York, 1975, ISBN 3-6429-7357-4 * Hans Herbert Klinger: ''Lautsprecher und Lautsprechergehäuse für HiFi'', Franzis-Verlag GmbH, München, 1981, ISBN 3-7723-1051-6 * Heinz Sahm: ''HiFi-Lautsprecher'', ''Grundlagen der elektrodynamischen Lautsprecher in unendlicher Schallwand und im Gehäuse'', Franzis-Verlag GmbH, München, 1982, ISBN 3-7723-6522-1 * Berndt Stark: ''Lautsprecher-Handbuch'' - ''Theorie und Praxis des Boxenbauens'', Richard Pflaum Verlag, München, 1985, ISBN 3-7905-0433-5 == Siehe auch == * [[Grundlegendes zur Akustik]] == Weblinks == {{Commonscat|Transmission line loudspeakers}} == Zusammenfassung des Projekts == {{Vorlage:StatusBuch|10}} * '''Zielgruppe und Lernziele:''' Dieses Buch richtet sich an akustisch interessierte Leser mit mathematischer Ausbildung. Es beschreibt einen vollständigen Weg zur Berechnung einer bedämpften akustischen Transmissionline mit komplexwertigen Funktionen. Aufbauend auf grundlegenden Betrachtungen zur Schallerzeugung und -abstrahlung von Lautsprechern wird durch schrittweise Erweiterungen und Ergänzungen die Funktionsweise einer solchen Transmissionline erarbeitet. * '''Ansprechperson:''' Ansprechpartner ist [[Benutzer:Bautsch]] [[Kategorie:Geometrische Kuriositäten‎]] oxye9bw9nfcqbdtksr8vheif8v2vglg 1088122 1088121 2026-06-14T00:01:42Z Bautsch 35687 /* Erfahrungswerte für den Entwurf einer Transmissionline-Lautsprecherbox */ \rho \eta_m 1088122 wikitext text/x-wiki {{Regal | ort=Physik}} {{Infoleiste|Physik|Studium|S|Technik}} [[Bild:Transmission-line.png|rechts|mini|Beispiel für eine Transmissionline als akustischen Kanal (blaue Pfeile) für den rückwärtigen Schall eines Lautsprechers (orangefarben) in einer Lautsprecherbox (dunkelbraun).]] == Einführung == '''Transmissionline''' ("Übertragungsleitung") nennt man in der Lautsprecherakustik einen in der Regel mehrfach gefalteten Kanal, durch den der von einem Lautsprecher nach hinten abgegebene Schall hindurchtritt. Am Ende dieses Kanals befindet sich eine Öffnung, durch die der Schall heraustreten kann. Dieser Schall kann unter geeigneten Voraussetzungen dazu verwendet werden, den Amplituden-Frequenzgang einer elektrodynamischen Lautsprecherbox am tieffrequenten Ende zu erweitern und zu verbessern. Ausgehend von idealisierten theoretischen Ansätzen zum Abstrahlverhalten von Lautsprechern und mit der Theorie der quasihomogenen Absorber wird in diesem Buch darauf eingegangen, wie der Tieftonbereich einer Transmissionline-Lautsprecherbox dimensioniert und akustisch gedämpft werden kann. Dazu werden wegen des einfachen mathematischen Formalismus Funktionen komplexer Zahlen ({{w|Eulersche Formel}}) herangezogen. Dieses Wikibook basiert auf der privaten Vorveröffentlichung ''Anleitung zur akustischen Berechnung einer elektrodynamischen "transmission line"-Lautsprecherbox'' aus dem Jahr 2000. Das hier vorgestellte Berechnungsverfahren wurde vom Autor im Jahr 1986 entwickelt und für den Bau von Transmissionline-Lautsprecherboxen angewandt. <div style="clear:both"></div> == Abstrahlverhalten von Lautsprechern == [[Bild:Amplitudenfrequenzgang.Lautsprecher.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang eines elektrodynamischen Lautsprechers.]] Der Amplitudenfrequenzgang <math>A_0</math> eines elektrodynamischen Lautsprechers mit der Gesamtgüte <math>Q</math> und der Eigenfrequenz <math>f_0</math> berechnet sich über der Schallfrequenz <math>f</math> theoretisch zu :<math>A_0 (f) = \frac {\frac {f} {f_0}} {\sqrt {\frac {1} {Q^2} + \left(\frac {f} {f_0} - \frac {f_0} {f}\right)^2}}</math> Im Resonanzfall <math>f = f_0</math> vereinfacht sich diese Gleichung zu :<math>A_0 (f_0) = Q</math> Mit zunehmender Frequenz fällt der Frequenzgang jedoch entgegen der Aussage obiger Gleichung wieder ab, da die Lautsprechermembran wegen ihrer mechanischen Trägheit nicht mehr mitschwingen kann. Weitere Abweichungen ergeben sich in der Praxis, weil auch andere Eigenfrequenzen als <math>f_0</math> noch kleine Beiträge zum Frequenzgang leisten. Diese Eigenfrequenzen werden im Allgemeinen durch ungewollte Schwingungsmoden der Lautsprechermembran verursacht. <div style="clear:both"></div> === Freier Lautsprecher === [[Bild:Freier.Lautsprecher.gif|upright=2|miniatur|rechts|Freier Lautsprecher.]] Wird ein Lautsprecher bei der Frequenz <math>f</math> ohne Schallwand oder Gehäuse betrieben, so interferieren an der Stelle <math>x = 0</math> (also an der Lautsprecheroberfläche) über der Zeit <math>t</math> die nach vorne abgegebene Schallwelle :<math>A_v = A(f,t) = A_0(f) \cdot e^{-i \omega t}</math> und die nach hinten abgestrahlte Schallwelle :<math>A_h = -A(f,t) = -A_0(f) \cdot e^{-i \omega t}</math> mit der {{w|Eulersche Zahl|Eulerschen Zahl}} <math>e</math>, der {{w|Imaginäre Zahl|imaginären Einheit}} <math>i = \sqrt {-1}</math> und der {{w|Kreisfrequenz}} <math>\omega = 2 \pi f</math> zu allen Zeitpunkten destruktiv: :<math>A_{ges} = A_v + A_h = 0</math> Dies bedeutet, dass die gesamte Amplitudenfrequenzgang <math>A_{ges}</math> null wird, da sich die beiden Schallwellen gegenseitig auslöschen. Dies wird auch als {{w|Akustischer Kurzschluss|akustischer Kurzschluss}} bezeichnet. <div style="clear:both"></div> === Lautsprecher mit Schallwand === [[Bild:Lautsprecher.mit.Schallwand.gif|upright=2|miniatur|rechts|Lautsprecher mit unendlich ausgedehnter Schallwand.]] Die Probleme beim freien Lautsprecher können umgangen werden, wenn der Lautsprecher in eine im Verhältnis zur größten abzustrahlenden Schallwellenlänge möglichst weit ausgedehnte Schallwand eingebaut wird. Allerdings muss eine solche Schallwand meist sehr große Abmessungen haben. Bei einer minimalen Frequenz von beispielsweise 30&nbsp;Hertz müsste die Schallwand seitlich mehrere Meter Ausdehnung haben. :<math>A_{ges} = A_v = A(f,t) = A_0(f)\cdot e^{-i \omega t}</math> wobei <math>A_v</math> die nach vorne abgestrahlte Schallwelle repräsentiert, die in diesem Fall mit der gesamten abgestrahlten <math>A_{ges}</math> identisch ist. <div style="clear:both"></div> === Schallabyrinth === [[Bild: Schallabyrinth.gif|upright=2|miniatur|rechts|Schalllabyrinth.]] Eine Alternative wäre der Einbau des Lautsprechers in ein sogenanntes '''Schallabyrinth'''. Die Schallenergie der nach hinten abgegebenen Schallwelle wird hier durch Mehrfachreflexion und Mehrfachstreuung in Verbindung mit Absorption vernichtet. Auch ein solches Schallabyrinth muss jedoch sehr groß dimensioniert werden, damit der Schall weitgehend absorbiert werden kann. Wie auch bei der Schallwand besteht der Nachteil, dass die vom Lautsprecher nach hinten abgestrahlte Schallenergie nicht genutzt wird. <div style="clear:both"></div> == Geschlossene Lautsprecherbox == [[Bild:Geschlossene.Lautsprecherbox.gif|upright=2|miniatur|rechts|Geschlossene Lautsprecherbox.]] Das ideale Verhalten eines Schalllabyrinths wird bei '''Lautsprecherboxen in geschlossener Bauweise''' nicht erreicht. Neben der nach vorne abgestrahlten Schallwelle <math>A_v</math> können auch die nach hinten abgestrahlten Schallwellen <math>A_h</math>, die an den Gehäuseinnenwänden reflektiert werden, durch die Lautsprecheröffnung hinaustreten. Als Folge der Reflexionen treten ferner stehende Wellen auf, die sich negativ auf den Amplituden- und Phasenfrequenzgang der Lautsprecherbox auswirken. Dies kann zwar eingeschränkt werden, indem das Innere der Lautsprecherbox mit einem schallschluckenden Medium gefüllt wird und die Gehäusewände nicht parallel ausgerichtet sind, jedoch lässt sich dieser Effekt nie vollständig vermeiden. Ferner kann besonders bei niedrigen Frequenzen ein so hoher Schalldruck auftreten, dass eine Rückkopplung auf die Lautsprechermembran stattfindet, die sich ebenso wie die reflektierten Schallwellen als nichtlinearer Effekt negativ bemerkbar macht. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich aus der Überlagerung aller Schallwellen: :<math>A_{ges} = A_v + B(A_h) + C(A_h)</math>, wobei die Funktion <math>B(A_h)</math> die im Lautsprechergehäuse gedämpften und reflektieren Schallwellen repräsentiert und <math>C(A_h)</math> die nichtlinearen Anteile durch Rückkopplung des Schalls. <div style="clear:both"></div> === Teilventilierte Lautsprecherbox === [[Bild:Teilventilierte.Lautsprecherbox.gif|upright=2|miniatur|rechts|Teilventilierte Lautsprecherbox.]] Der hohe Schalldruck wird bei den sogenannten '''teilventilierten Lautsprecherboxen''' durch eine kleine Öffnung vermieden, die zum Druckausgleich dient. Wenn solche Lautsprecherboxen gut bedämpft sind und keine parallelen Gehäusewände haben, damit sich keine stehenden Wellen aufbauen können, stellen sie einen recht guten Kompromiss dar, die nach hinten abgestrahlte Schallenergie findet aber auch hier keine Verwendung bei der Klangwiedergabe. <div style="clear:both"></div> === Bass-Reflex-Lautsprecherbox === [[Bild:Bass.Reflex.Lautsprecherbox.gif|upright=2|miniatur|rechts|Bass-Reflex-Lautsprecherbox.]] Bei '''Bass-Reflex-Lautsprecherboxen''' wird die Eigenfrequenz des Lautsprechergehäuses bewusst eingesetzt, um die Schallabstrahlung der Lautsprecherbox meist im Tieftonbereich zu verstärken. Die Lautsprecherbox hat eine zusätzliche, meist nach vorn gerichtete Rohröffnung und wirkt somit als Helmholtz-Resonator mit der Amplitudenfunktion <math>B_v(f,t)</math>. Es ist auf diese Weise jedoch problematisch, einen gleichmäßigen Frequenzgang zu erreichen, da in der Regel eine zu starke Verstärkung in einem zu schmalen Frequenzband besteht und dadurch die beiden nach vorne abgestrahlten Schallwellen vom Lautsprecher <math>A_v</math> und von der zusätzlichen Öffnung <math>B_v</math> sehr schlecht aufeinander abgestimmt werden können. Auch bei Bass-Reflex-Lautsprecherboxen kann es wie bei geschlossenen Lautsprecherboxen natürlich zusätzlich noch zu stehenden Wellen kommen. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich also im Wesentlichen aus der Überlagerung zweier Schallwellen: :<math>A_{ges} = A_v + B_v</math>, wobei die Funktion <math>A_v</math> die nach vorne abgestrahlte Schallwelle und <math>B_v</math> die aus der Bass-Reflex-Öffnung abgestrahlten Schallwellen repräsentiert. <div style="clear:both"></div> == Transmissionline-Lautsprecherbox == === Transmissionline ohne Dämpfung === [[Bild:Transmissionline.Lautsprecherbox.ohne.Daempfung.gif|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox ohne Dämpfung.]] Die vom Lautsprecher nach hinten abgegebene Schallwelle <math>A_h</math> kann durch einen Schallkanal mit der Länge <math>X_0</math> geleitet werden, an dessen Ende sich eine Öffnung befindet. Dieser idealerweise resonanzfreie Schallkanal wurde von dem an der Universität von Bradford lehrenden Briten Arthur R.&nbsp;Bailey '''Transmissionline''' genannt. Die aus dieser Öffnung heraustretende Schallwelle <math>A_{h,TL}</math> kann mit der vom Lautsprecher nach vorn abgestrahlten Schallwelle <math>A_v</math> interferieren. Die Amplitude der interferierten Schallwelle ergibt sich im Falle des ungedämpften Schallkanals aus der Summe der beiden Anteile: :<math>A_v = A_0(f)</math> wie beim freien Lautsprecher und :<math>A_{h,TL} = -A_0(f)\cdot e^{-i \omega \cdot {X_0 \over c_0}}</math> Hierbei ist <math>c_0</math> die Schallgeschwindigkeit im gasförmigen Schallmedium (in Luft bei 20°&nbsp;C und 60% relativer Luftfeuchtigkeit ist <math>c_0 = 344 {\frac {m} {s}}</math>). An der Lautsprecherfront ergibt sich für <math>A_{ges,TL}</math> ohne Dämpfung also: :<math>A_{ges,TL} = A_v + A_{h,TL} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {\frac {X_0} {c_0}}}\right)</math> <div style="clear:both"></div> === Transmissionline mit Dämpfung === ==== Qualitative Beschreibung ==== In den 1960er Jahren kam Bailey auf die Idee, die von einem Tieftöner nach hinten abgestrahlte Schallenergie in einem hinreichend langen, bedämpften und am Ende offenen Schallkanal quasi "totlaufen" zu lassen, was er in zwei Artikeln im Fachmagazin Wireless World beschrieben hat. Die Dämpfung ist dabei ein integraler Bestandteil der Konstruktion von Bailey. Die Dämpfung, die Schallgeschwindigkeit und somit auch die Wellenlänge der Schallwellen sind bei einem solchen Tiefpass frequenzabhängig (Dispersion). Die Schallwellen werden umso weniger stark gedämpft, je geringer die Frequenz beziehungsweise je größer die Wellenlänge der Schallwellen ist. Die höherfrequenten Anteile werden durch die akustische Dämpfung in Wärmeenergie umgewandelt, und die sehr tieffrequenten am Ende aus der Transmissionline noch austretenden Schallwellen können bei geeigneter Interferenz mit den direkten, nach vorne abgegebenen Schallwellen des eingesetzten Tieftöners den Amplitudenfrequenzgang bei tiefen Frequenzen verbessern. Die ersten nach diesem Prinzip gebauten kommerziellen Lautsprecherboxen wurden zunächst von der in Cambridgeshire ansässigen und mit Bailey kooperierenden Firma Radford und später unter anderem von IMF Electronics und in Deutschland von {{w|Lautsprecher Teufel}} auf den Markt gebracht. Die vom Lautsprecher nach hinten abgegebene Schallwelle wird in dem zum Beispiel mit langfaseriger Schafwolle gedämmten Schallkanal zunehmend abgeschwächt, sehr tiefe Frequenzen können den Schallkanal jedoch nur schwach gedämpft verlassen und zur Schallwiedergabe verwendet werden. Die Länge des Kanals sollte nach Bailey mindestens etwa ein Viertel der Wellenlänge der unteren Grenzfrequenz betragen, kann aber auch länger sein. Er kann in verschiedener Weise geknickt oder gefaltet werden, um in einem konventionellen Gehäuse Platz zu finden, jedoch sollten Reflexionen (zum Beispiel mit gekrümmten oder abgeschrägten Flächen) vermieden werden. Da bei diesem "nicht-resonanten Lautsprechergehäuse" (Bailey) ein großer Teil der nach hinten abgestrahlte Schallenergie vernichtet wird, ist der Wirkungsgrad von Transmissionline-Lautsprechern relativ schlecht, was jedoch dank leistungsfähiger (Transistor-)Verstärker normalerweise keine Rolle spielt. Bei korrekter Konstruktion zeichnen sich Transmissionline-Lautsprecher durch sehr gute, klangneutrale (Tief-)Basswiedergabe und sehr gutes Impulsverhalten aus. Wegen Größe, Gewicht und der relativ hohen Fertigungskosten konnten sie sich jedoch auf lange Sicht nicht auf dem Markt durchsetzen. <gallery caption="Tieffrequente Sinustöne mit 30 Sekunden Dauer" perrow="5"> Sine Wave 10Hz 30s.ogg|10 Hertz Sine Wave 20Hz 30s.ogg|20 Hertz Sine Wave 30Hz 30s.ogg|30 Hertz Sine Wave 40Hz 30s.ogg|40 Hertz Sine Wave 50Hz 30s.ogg|50 Hertz </gallery> ==== Quantitative Berechnung ==== [[Bild:Transmissionline.Lautsprecherbox.mit.Daempfung.png|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox mit Dämpfung.]] Im folgenden werden die von Bailey im Wesentlichen nur qualitativ beschriebenen Sachverhalte anhand entsprechender akustischer Formeln quantitativ beschrieben. Wird die Transmissionline mit einem porösen, faserigen Absorber bedämpft, so kann die aus der Transmissionline austretende Schallwellenamplitude <math>A_{h,TL,D}</math> nach Fridolin Peter Mechel mit der '''Theorie der quasihomogenen Absorber''' näherungsweise angegeben werden: :<math>A_{h,TL,D} = -A_0 (f)\cdot e^{-i \omega \cdot {X_0 \over c_0} \cdot \sqrt{1 - i \cdot {r \over {\rho_0 \cdot \omega}}}}</math> Dabei ist <math>\rho_0</math> die Dichte des gasförmigen Schallmediums (bei trockener Luft bei 20°&nbsp;C ist <math>\rho_0 = 1.2 {\text{kg} \over \text{m}^3}</math>) und <math>r</math> der spezifische Strömungswiderstand des porösen Absorbermaterials in Newtonsekunden pro Biquadratmeter. Je größer der spezifische Strömungswiderstand, desto stärker ist die Schalldämpfung. Er hängt im Wesentlichen vom Material, von der Faserfeinheit und von der Massendichte <math>\rho</math>, also von der Packungsdichte, des Absorbers und vom Füllgrad in der Transmissionline ab. Weiterhin hängt er von der gleichmäßigen Verteilung des Absorbers aber auch von Faserrichtung und der Schallfrequenz ab, so dass in der folgenden Tabelle nur ungefähre Richtwerte für einige poröse, faserige Absorber angeben sind: {| class="wikitable zebra" | style="text-align:center" !Material !Massendichte !Spezifischer<br/>Strömungswiderstand !Gewichtseffizienz |- | |<math>\rho</math> in <math>\frac {\text{kg}} {\text{m}^3}</math> |<math>r</math> in <math>\frac {\text{Ns}} {\text{m}^4}</math> |<math>\eta = \frac {r} {\rho}</math> in <math>\frac {\text{Ns}} {\text{kg m}} = \text{Hz}</math> |- |Schafwolle |5 |4000 |800 |- |Schafwolle |10 |8000 |800 |- |Baumwolle |5 |1000 |200 |- |Baumwolle |10 |4000 |400 |- |Glaswolle |50 |200 |4 |- |Glasfaserplatten |20 |1000 |50 |- |Mineralwolle |10 |600 |60 |- |Mineralwolle |50 |10000 |200 |- |Aluminiumwolle |35 |500 |14,3 |- |Aluminiumwolle |70 |4000 |57,1 |} Die Gewichtseffizienz des Absorbermaterials <math>\eta</math> ist in der Tabelle als Verhältnis der spezifischen Strömungswiderstands <math>r</math> zur Massendichte <math>\rho</math> angegeben: :<math>\eta = \frac {r} {\rho}</math> [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Laengen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Längen.]] [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Daempfungen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Dämpfungen.]] Für die gesamte Transmissionline-Lautsprecherbox ergibt sich an der Front der Lautsprecherbox mit Dämpfung die folgende frequenzabhängige Amplitude: :<math>A_{ges,TL,D} = A_v + A_{h,TL,D} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {X_0 \over c_0} \cdot \sqrt{1 - i \cdot {r \over {\rho_0 \cdot \omega}}}}\right)</math> Der Amplitudenverlauf ergibt sich aus dem Betrag der komplexen Funktion <math>A_{ges,TL,D}</math>. Dieser muss entsprechend den Parametern Gesamtgüte des Tieftöners <math>Q</math>, Länge der Transmissionline <math>X_0</math> und spezifischer Strömungswiderstand <math>r</math> so angepasst werden, dass bei den tiefen Frequenzen ein möglichst gleichmäßiger Amplitudenverlauf entsteht. Die beiden nebenstehenden Abbildungen sollen den Einfluss der beiden Parameter <math>X_0</math> und <math>r</math> bei vorgegebenem <math>Q</math> verdeutlichen. Die als geeignet bestimmten Parameter können beim Bau einer Transmissionline-Lautsprecherbox umgesetzt werden. Insbesondere die richtige Bedämpfung muss hierbei jedoch experimentell überprüft und gegebenenfalls korrigiert werden. Ferner muss bei der Ansteuerung von Tief- und Mitteltöner die aus obiger Gleichung resultierende Amplitude :<math>|A_{ges,TL,D}|</math> und Phasendifferenz :<math>arg(A_{ges,TL,D})</math> bei der Übergangsfrequenz zwischen dem System aus Transmissionline und Tieftöner auf der einen Seite und Mitteltöner auf der anderen Seite mittels der Frequenzweiche ausgeglichen werden. <div style="clear:both"></div> === Erfahrungswerte für den Entwurf einer Transmissionline-Lautsprecherbox === [[Datei:Transmissionline-Lautsprecher.P1117437.jpg|mini|hochkant=1.5|Transmissionline-Lautsprecherbox aus dem Jahr 1986 mit trapezförmiger Grundfläche, oben der Tieftöner, darunter der Mitteltöner und unten der Hochtöner. Die beiden Öffnungen der unten im Gehäuse symmetrisch nach rechts und links aufgeteilten akustischen Transmissionlines befinden sich oben an den abgeschrägten Seiten hinter dem Tieftöner. Rechnerisch wird im Frequenzgang beim Unterschreiten von 30&nbsp;Hertz ein Pegelabfall von drei Dezibel erreicht. In einem größeren Abhöraum betrug die Pegelschwankung der Kombination aus Tieftöner und Transmissionline zwischen 20&nbsp;und 50&nbsp;Hertz ungefähr ein Dezibel.]] Der Querschnitt der Transmissionline direkt hinter dem Lautsprecher sollte mindestens so groß sein wie die Lautsprechermembranfläche oder sogar etwas darüber liegen. Entlang der Transmissionline kann sich deren Querschnitt um maximal 25&nbsp;Prozent verringern. Für den Transmissionline-Betrieb geeignete Tieftöner sollten eine relativ hohe Gesamtgüte <math>\left(Q \lessapprox 1\right)</math> haben. Mit einer passiven Frequenzweiche, über aktive Frequenzfilter beziehungsweise mit Hilfe von digitalen Signalprozessoren werden die elektrischen Signale auf die Tief- und Mittel- sowie gegebenenfalls auch die Hochtöner verteilt. Um unerwünschte Effekte durch akustische Interferenzen zu minimieren, sollten die seitlichen Abstände zwischen Mittel- und Tieftöner sowie zwischen Hoch- und Mitteltöner so gering wie möglich gehalten werden. Außerdem sollten alle Lautsprecher bündig mit dem Gehäuse abschließen und in einer Ebene liegen. Die Austrittsöffnung einer Transmissionline kann konstruktiv wie ein Subtieftöner betrachtet werden. Der Wert des spezifischen Strömungswiderstands <math>r</math> (und somit der Dichte <math>\rho_0</math>) sollte bei der fertiggestellten Lautsprecherbox experimentell überprüft (zum Beispiel mit Frequenzgenerator oder mit Rauschgenerator und Frequenzanalysator) und gegebenenfalls angepasst werden, da er für einen in die Transmissionline eingebauten Absorber meist nicht genau genug berechnet werden kann. Der Mess- oder Abhörraum hat durch seine Eigenfrequenzen im tiefen Frequenzbereich einen deutlichen Einfluss auf die Tieftonwiedergabe. Insbesondere bei parallelen Wänden (respektive Böden und Decken) können stehende Wellen Raumresonanzen hervorrufen, wenn die Schallwellenlänge <math>\lambda</math> dem Doppelten einer Raumdimension mit der Länge <math>s</math> entspricht: :<math>f_{Resonanz} = \frac {c_0} {\lambda} = \frac {c_0} {2 \cdot s}</math> In einem quaderförmigen Raum mit der Länge <math>l</math>, der Breite <math>b</math> und der Höhe <math>h</math> ergibt sich nach dem Satz der Pythagoras die kleinste Resonanzfrequenz aus der Raumdiagonale <math>d</math>: :<math>d = \sqrt {l^2 + b^2 + h^2}</math> :<math>f_{Resonanz, min} = \frac {c_0} {2 \cdot d} = \frac {c_0} {2 \cdot \sqrt {l^2 + b^2 + h^2}}</math> Ein quaderförmiger Raum mit einer quadratischen Grundfläche von 100&nbsp;Quadratmetern hat demnach eine niedrigste Eigenfrequenz von ungefähr 12&nbsp;Hertz. Durch schwere Vorhänge und Teppiche oder andere Schallabsorber kann die Schallreflexion in einem Raum deutlich reduziert werden, wobei die Schallabsorption für tiefer werdende Frequenzen in der Regel zunehmend geringer wird. Im freien Feld beziehungsweise in schallreflexionsarmen Messräumen entfallen solche Raumreflexionen oder sind so stark reduziert, so dass sich für einen gegebenen Lautsprecher ein anderes Klangbild als in einem gewöhnlichen Raum ergibt. {| class="wikitable" |+ Parameter der abgebildeten Transmissionline-Lautsprecherbox ! Parameter !! Formelsymbol !! Wert |- | Eigenresonanzfrequenz des Tieftöners || <math>f_0</math> || <math>\text {31 Hz}</math> |- | Güte des Tieftöners || <math>Q</math> || <math>\text {0,65}</math> |- | Länge der Transmissionline || <math>X_0</math> || <math>\text {2,75 m}</math> |- | Mittlerer spezifischer Widerstand der Dämmung mit Mineralwolle || <math>r</math> || <math>\text {1500 } \frac {\text {Ns}} {\text {m}^4}</math> |- | Mittlere Massendichte der Dämmung mit Mineralwolle || <math>\rho</math> || <math>\text {17,4 } \frac {\text {kg}} {\text {m}^3}</math> |- | Gewichtseffizienz der Dämmung mit Mineralwolle || <math>\eta_m</math> || <math>\text {86 } \text {Hz}</math> |} ==Widmung== Diese Zusammenstellung ist meinem Studienfreund '''Dr.&nbsp;Lutz König''' gewidmet. Der Hauptautor dankt ihm für seine freundschaftlichen, umfangreichen und stets förderlichen Beiträge zur Realisierung unserer 1986 in Eigenentwicklung hergestellten vier Lautsprecherboxen. == Literatur == * Arthur R.&nbsp;Bailey: ''A Non-resonant Loudspeaker Enclosure Design'' - ''Using acoustic transmission line with low-pass filter characteristics'', Wireless World, October 1965, p.&nbsp;483-486 * Arthur R.&nbsp;Bailey: ''The Transmission-line Loudspeaker Enclosure'' - ''A re-examination of the general principle and a suggested new method of construction'', Wireless World, May 1972, p.&nbsp;215-217 * {{w|Ludwig Bergmann (Physiker)|Ludwig Bergmann}}, {{w|Clemens Schaefer (Physiker)|Clemens Schäfer}}: ''{{w|Bergmann-Schaefer_Lehrbuch_der_Experimentalphysik|Lehrbuch der Experimentalphysik}}'', Band 1: ''Mechanik, Akustik, Wärme'', 9. Auflage, de Gruyter, 1974, ISBN 978-3-1100-4861-2 * Fridolin P.&nbsp;Mechel: ''Schallabsorption'', Kapitel&nbsp;18, in: {{w|Manfred Heckl}}, {{w|Helmut A. Müller (Akustiker)|Helmut A.&nbsp;Müller}}: ''Taschenbuch der technischen Akustik'', Springer-Verlag, Berlin, Heidelberg, New York, 1975, ISBN 3-6429-7357-4 * Hans Herbert Klinger: ''Lautsprecher und Lautsprechergehäuse für HiFi'', Franzis-Verlag GmbH, München, 1981, ISBN 3-7723-1051-6 * Heinz Sahm: ''HiFi-Lautsprecher'', ''Grundlagen der elektrodynamischen Lautsprecher in unendlicher Schallwand und im Gehäuse'', Franzis-Verlag GmbH, München, 1982, ISBN 3-7723-6522-1 * Berndt Stark: ''Lautsprecher-Handbuch'' - ''Theorie und Praxis des Boxenbauens'', Richard Pflaum Verlag, München, 1985, ISBN 3-7905-0433-5 == Siehe auch == * [[Grundlegendes zur Akustik]] == Weblinks == {{Commonscat|Transmission line loudspeakers}} == Zusammenfassung des Projekts == {{Vorlage:StatusBuch|10}} * '''Zielgruppe und Lernziele:''' Dieses Buch richtet sich an akustisch interessierte Leser mit mathematischer Ausbildung. Es beschreibt einen vollständigen Weg zur Berechnung einer bedämpften akustischen Transmissionline mit komplexwertigen Funktionen. Aufbauend auf grundlegenden Betrachtungen zur Schallerzeugung und -abstrahlung von Lautsprechern wird durch schrittweise Erweiterungen und Ergänzungen die Funktionsweise einer solchen Transmissionline erarbeitet. * '''Ansprechperson:''' Ansprechpartner ist [[Benutzer:Bautsch]] [[Kategorie:Geometrische Kuriositäten‎]] qwrzo8kwkcdymuwldrdsal8emxb2frl 1088123 1088122 2026-06-14T00:24:58Z Bautsch 35687 /* Quantitative Berechnung */ Gamma Omega 1088123 wikitext text/x-wiki {{Regal | ort=Physik}} {{Infoleiste|Physik|Studium|S|Technik}} [[Bild:Transmission-line.png|rechts|mini|Beispiel für eine Transmissionline als akustischen Kanal (blaue Pfeile) für den rückwärtigen Schall eines Lautsprechers (orangefarben) in einer Lautsprecherbox (dunkelbraun).]] == Einführung == '''Transmissionline''' ("Übertragungsleitung") nennt man in der Lautsprecherakustik einen in der Regel mehrfach gefalteten Kanal, durch den der von einem Lautsprecher nach hinten abgegebene Schall hindurchtritt. Am Ende dieses Kanals befindet sich eine Öffnung, durch die der Schall heraustreten kann. Dieser Schall kann unter geeigneten Voraussetzungen dazu verwendet werden, den Amplituden-Frequenzgang einer elektrodynamischen Lautsprecherbox am tieffrequenten Ende zu erweitern und zu verbessern. Ausgehend von idealisierten theoretischen Ansätzen zum Abstrahlverhalten von Lautsprechern und mit der Theorie der quasihomogenen Absorber wird in diesem Buch darauf eingegangen, wie der Tieftonbereich einer Transmissionline-Lautsprecherbox dimensioniert und akustisch gedämpft werden kann. Dazu werden wegen des einfachen mathematischen Formalismus Funktionen komplexer Zahlen ({{w|Eulersche Formel}}) herangezogen. Dieses Wikibook basiert auf der privaten Vorveröffentlichung ''Anleitung zur akustischen Berechnung einer elektrodynamischen "transmission line"-Lautsprecherbox'' aus dem Jahr 2000. Das hier vorgestellte Berechnungsverfahren wurde vom Autor im Jahr 1986 entwickelt und für den Bau von Transmissionline-Lautsprecherboxen angewandt. <div style="clear:both"></div> == Abstrahlverhalten von Lautsprechern == [[Bild:Amplitudenfrequenzgang.Lautsprecher.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang eines elektrodynamischen Lautsprechers.]] Der Amplitudenfrequenzgang <math>A_0</math> eines elektrodynamischen Lautsprechers mit der Gesamtgüte <math>Q</math> und der Eigenfrequenz <math>f_0</math> berechnet sich über der Schallfrequenz <math>f</math> theoretisch zu :<math>A_0 (f) = \frac {\frac {f} {f_0}} {\sqrt {\frac {1} {Q^2} + \left(\frac {f} {f_0} - \frac {f_0} {f}\right)^2}}</math> Im Resonanzfall <math>f = f_0</math> vereinfacht sich diese Gleichung zu :<math>A_0 (f_0) = Q</math> Mit zunehmender Frequenz fällt der Frequenzgang jedoch entgegen der Aussage obiger Gleichung wieder ab, da die Lautsprechermembran wegen ihrer mechanischen Trägheit nicht mehr mitschwingen kann. Weitere Abweichungen ergeben sich in der Praxis, weil auch andere Eigenfrequenzen als <math>f_0</math> noch kleine Beiträge zum Frequenzgang leisten. Diese Eigenfrequenzen werden im Allgemeinen durch ungewollte Schwingungsmoden der Lautsprechermembran verursacht. <div style="clear:both"></div> === Freier Lautsprecher === [[Bild:Freier.Lautsprecher.gif|upright=2|miniatur|rechts|Freier Lautsprecher.]] Wird ein Lautsprecher bei der Frequenz <math>f</math> ohne Schallwand oder Gehäuse betrieben, so interferieren an der Stelle <math>x = 0</math> (also an der Lautsprecheroberfläche) über der Zeit <math>t</math> die nach vorne abgegebene Schallwelle :<math>A_v = A(f,t) = A_0(f) \cdot e^{-i \omega t}</math> und die nach hinten abgestrahlte Schallwelle :<math>A_h = -A(f,t) = -A_0(f) \cdot e^{-i \omega t}</math> mit der {{w|Eulersche Zahl|Eulerschen Zahl}} <math>e</math>, der {{w|Imaginäre Zahl|imaginären Einheit}} <math>i = \sqrt {-1}</math> und der {{w|Kreisfrequenz}} <math>\omega = 2 \pi f</math> zu allen Zeitpunkten destruktiv: :<math>A_{ges} = A_v + A_h = 0</math> Dies bedeutet, dass die gesamte Amplitudenfrequenzgang <math>A_{ges}</math> null wird, da sich die beiden Schallwellen gegenseitig auslöschen. Dies wird auch als {{w|Akustischer Kurzschluss|akustischer Kurzschluss}} bezeichnet. <div style="clear:both"></div> === Lautsprecher mit Schallwand === [[Bild:Lautsprecher.mit.Schallwand.gif|upright=2|miniatur|rechts|Lautsprecher mit unendlich ausgedehnter Schallwand.]] Die Probleme beim freien Lautsprecher können umgangen werden, wenn der Lautsprecher in eine im Verhältnis zur größten abzustrahlenden Schallwellenlänge möglichst weit ausgedehnte Schallwand eingebaut wird. Allerdings muss eine solche Schallwand meist sehr große Abmessungen haben. Bei einer minimalen Frequenz von beispielsweise 30&nbsp;Hertz müsste die Schallwand seitlich mehrere Meter Ausdehnung haben. :<math>A_{ges} = A_v = A(f,t) = A_0(f)\cdot e^{-i \omega t}</math> wobei <math>A_v</math> die nach vorne abgestrahlte Schallwelle repräsentiert, die in diesem Fall mit der gesamten abgestrahlten <math>A_{ges}</math> identisch ist. <div style="clear:both"></div> === Schallabyrinth === [[Bild: Schallabyrinth.gif|upright=2|miniatur|rechts|Schalllabyrinth.]] Eine Alternative wäre der Einbau des Lautsprechers in ein sogenanntes '''Schallabyrinth'''. Die Schallenergie der nach hinten abgegebenen Schallwelle wird hier durch Mehrfachreflexion und Mehrfachstreuung in Verbindung mit Absorption vernichtet. Auch ein solches Schallabyrinth muss jedoch sehr groß dimensioniert werden, damit der Schall weitgehend absorbiert werden kann. Wie auch bei der Schallwand besteht der Nachteil, dass die vom Lautsprecher nach hinten abgestrahlte Schallenergie nicht genutzt wird. <div style="clear:both"></div> == Geschlossene Lautsprecherbox == [[Bild:Geschlossene.Lautsprecherbox.gif|upright=2|miniatur|rechts|Geschlossene Lautsprecherbox.]] Das ideale Verhalten eines Schalllabyrinths wird bei '''Lautsprecherboxen in geschlossener Bauweise''' nicht erreicht. Neben der nach vorne abgestrahlten Schallwelle <math>A_v</math> können auch die nach hinten abgestrahlten Schallwellen <math>A_h</math>, die an den Gehäuseinnenwänden reflektiert werden, durch die Lautsprecheröffnung hinaustreten. Als Folge der Reflexionen treten ferner stehende Wellen auf, die sich negativ auf den Amplituden- und Phasenfrequenzgang der Lautsprecherbox auswirken. Dies kann zwar eingeschränkt werden, indem das Innere der Lautsprecherbox mit einem schallschluckenden Medium gefüllt wird und die Gehäusewände nicht parallel ausgerichtet sind, jedoch lässt sich dieser Effekt nie vollständig vermeiden. Ferner kann besonders bei niedrigen Frequenzen ein so hoher Schalldruck auftreten, dass eine Rückkopplung auf die Lautsprechermembran stattfindet, die sich ebenso wie die reflektierten Schallwellen als nichtlinearer Effekt negativ bemerkbar macht. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich aus der Überlagerung aller Schallwellen: :<math>A_{ges} = A_v + B(A_h) + C(A_h)</math>, wobei die Funktion <math>B(A_h)</math> die im Lautsprechergehäuse gedämpften und reflektieren Schallwellen repräsentiert und <math>C(A_h)</math> die nichtlinearen Anteile durch Rückkopplung des Schalls. <div style="clear:both"></div> === Teilventilierte Lautsprecherbox === [[Bild:Teilventilierte.Lautsprecherbox.gif|upright=2|miniatur|rechts|Teilventilierte Lautsprecherbox.]] Der hohe Schalldruck wird bei den sogenannten '''teilventilierten Lautsprecherboxen''' durch eine kleine Öffnung vermieden, die zum Druckausgleich dient. Wenn solche Lautsprecherboxen gut bedämpft sind und keine parallelen Gehäusewände haben, damit sich keine stehenden Wellen aufbauen können, stellen sie einen recht guten Kompromiss dar, die nach hinten abgestrahlte Schallenergie findet aber auch hier keine Verwendung bei der Klangwiedergabe. <div style="clear:both"></div> === Bass-Reflex-Lautsprecherbox === [[Bild:Bass.Reflex.Lautsprecherbox.gif|upright=2|miniatur|rechts|Bass-Reflex-Lautsprecherbox.]] Bei '''Bass-Reflex-Lautsprecherboxen''' wird die Eigenfrequenz des Lautsprechergehäuses bewusst eingesetzt, um die Schallabstrahlung der Lautsprecherbox meist im Tieftonbereich zu verstärken. Die Lautsprecherbox hat eine zusätzliche, meist nach vorn gerichtete Rohröffnung und wirkt somit als Helmholtz-Resonator mit der Amplitudenfunktion <math>B_v(f,t)</math>. Es ist auf diese Weise jedoch problematisch, einen gleichmäßigen Frequenzgang zu erreichen, da in der Regel eine zu starke Verstärkung in einem zu schmalen Frequenzband besteht und dadurch die beiden nach vorne abgestrahlten Schallwellen vom Lautsprecher <math>A_v</math> und von der zusätzlichen Öffnung <math>B_v</math> sehr schlecht aufeinander abgestimmt werden können. Auch bei Bass-Reflex-Lautsprecherboxen kann es wie bei geschlossenen Lautsprecherboxen natürlich zusätzlich noch zu stehenden Wellen kommen. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich also im Wesentlichen aus der Überlagerung zweier Schallwellen: :<math>A_{ges} = A_v + B_v</math>, wobei die Funktion <math>A_v</math> die nach vorne abgestrahlte Schallwelle und <math>B_v</math> die aus der Bass-Reflex-Öffnung abgestrahlten Schallwellen repräsentiert. <div style="clear:both"></div> == Transmissionline-Lautsprecherbox == === Transmissionline ohne Dämpfung === [[Bild:Transmissionline.Lautsprecherbox.ohne.Daempfung.gif|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox ohne Dämpfung.]] Die vom Lautsprecher nach hinten abgegebene Schallwelle <math>A_h</math> kann durch einen Schallkanal mit der Länge <math>X_0</math> geleitet werden, an dessen Ende sich eine Öffnung befindet. Dieser idealerweise resonanzfreie Schallkanal wurde von dem an der Universität von Bradford lehrenden Briten Arthur R.&nbsp;Bailey '''Transmissionline''' genannt. Die aus dieser Öffnung heraustretende Schallwelle <math>A_{h,TL}</math> kann mit der vom Lautsprecher nach vorn abgestrahlten Schallwelle <math>A_v</math> interferieren. Die Amplitude der interferierten Schallwelle ergibt sich im Falle des ungedämpften Schallkanals aus der Summe der beiden Anteile: :<math>A_v = A_0(f)</math> wie beim freien Lautsprecher und :<math>A_{h,TL} = -A_0(f)\cdot e^{-i \omega \cdot {X_0 \over c_0}}</math> Hierbei ist <math>c_0</math> die Schallgeschwindigkeit im gasförmigen Schallmedium (in Luft bei 20°&nbsp;C und 60% relativer Luftfeuchtigkeit ist <math>c_0 = 344 {\frac {m} {s}}</math>). An der Lautsprecherfront ergibt sich für <math>A_{ges,TL}</math> ohne Dämpfung also: :<math>A_{ges,TL} = A_v + A_{h,TL} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {\frac {X_0} {c_0}}}\right)</math> <div style="clear:both"></div> === Transmissionline mit Dämpfung === ==== Qualitative Beschreibung ==== In den 1960er Jahren kam Bailey auf die Idee, die von einem Tieftöner nach hinten abgestrahlte Schallenergie in einem hinreichend langen, bedämpften und am Ende offenen Schallkanal quasi "totlaufen" zu lassen, was er in zwei Artikeln im Fachmagazin Wireless World beschrieben hat. Die Dämpfung ist dabei ein integraler Bestandteil der Konstruktion von Bailey. Die Dämpfung, die Schallgeschwindigkeit und somit auch die Wellenlänge der Schallwellen sind bei einem solchen Tiefpass frequenzabhängig (Dispersion). Die Schallwellen werden umso weniger stark gedämpft, je geringer die Frequenz beziehungsweise je größer die Wellenlänge der Schallwellen ist. Die höherfrequenten Anteile werden durch die akustische Dämpfung in Wärmeenergie umgewandelt, und die sehr tieffrequenten am Ende aus der Transmissionline noch austretenden Schallwellen können bei geeigneter Interferenz mit den direkten, nach vorne abgegebenen Schallwellen des eingesetzten Tieftöners den Amplitudenfrequenzgang bei tiefen Frequenzen verbessern. Die ersten nach diesem Prinzip gebauten kommerziellen Lautsprecherboxen wurden zunächst von der in Cambridgeshire ansässigen und mit Bailey kooperierenden Firma Radford und später unter anderem von IMF Electronics und in Deutschland von {{w|Lautsprecher Teufel}} auf den Markt gebracht. Die vom Lautsprecher nach hinten abgegebene Schallwelle wird in dem zum Beispiel mit langfaseriger Schafwolle gedämmten Schallkanal zunehmend abgeschwächt, sehr tiefe Frequenzen können den Schallkanal jedoch nur schwach gedämpft verlassen und zur Schallwiedergabe verwendet werden. Die Länge des Kanals sollte nach Bailey mindestens etwa ein Viertel der Wellenlänge der unteren Grenzfrequenz betragen, kann aber auch länger sein. Er kann in verschiedener Weise geknickt oder gefaltet werden, um in einem konventionellen Gehäuse Platz zu finden, jedoch sollten Reflexionen (zum Beispiel mit gekrümmten oder abgeschrägten Flächen) vermieden werden. Da bei diesem "nicht-resonanten Lautsprechergehäuse" (Bailey) ein großer Teil der nach hinten abgestrahlte Schallenergie vernichtet wird, ist der Wirkungsgrad von Transmissionline-Lautsprechern relativ schlecht, was jedoch dank leistungsfähiger (Transistor-)Verstärker normalerweise keine Rolle spielt. Bei korrekter Konstruktion zeichnen sich Transmissionline-Lautsprecher durch sehr gute, klangneutrale (Tief-)Basswiedergabe und sehr gutes Impulsverhalten aus. Wegen Größe, Gewicht und der relativ hohen Fertigungskosten konnten sie sich jedoch auf lange Sicht nicht auf dem Markt durchsetzen. <gallery caption="Tieffrequente Sinustöne mit 30 Sekunden Dauer" perrow="5"> Sine Wave 10Hz 30s.ogg|10 Hertz Sine Wave 20Hz 30s.ogg|20 Hertz Sine Wave 30Hz 30s.ogg|30 Hertz Sine Wave 40Hz 30s.ogg|40 Hertz Sine Wave 50Hz 30s.ogg|50 Hertz </gallery> ==== Quantitative Berechnung ==== [[Bild:Transmissionline.Lautsprecherbox.mit.Daempfung.png|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox mit Dämpfung.]] Im folgenden werden die von Bailey im Wesentlichen nur qualitativ beschriebenen Sachverhalte anhand entsprechender akustischer Formeln quantitativ beschrieben. Wird die Transmissionline mit einem porösen, faserigen Absorber bedämpft, so kann die aus der Transmissionline mit der Länge <math>X_0</math> austretende Schallwellenamplitude <math>A_{h,TL,D}</math> nach Fridolin Peter Mechel nach der '''Theorie der quasihomogenen Absorber''' mit der komplexwertigen Ausbreitungskonstante <math>\Gamma</math> näherungsweise angegeben werden. Die Hilfsgröße <math>\Omega</math> steht hierbei für das Verhältnis des Trägheitswiderstands <math>t = \rho_0 \cdot \omega</math> zum Reibungswiderstand <math>r</math>, was ähnlich wie eine Reynolds-Zahl interpretiert werden kann: :<math>\Omega = \frac {t} {r} = \frac {\rho_0 \cdot \omega} {r}</math> :<math>\Gamma = \frac {i \omega} {c_0} \cdot \sqrt {1 - \frac {i} {\Omega}} = \frac {i \omega} {c_0} \cdot \sqrt {1 - i \cdot \frac {r} {\rho_0 \cdot \omega}}</math> :<math>A_{h,TL,D} = -A_0 (f) \cdot e^ {- \Gamma \cdot X_0} = -A_0 (f) \cdot e^ {-i \omega \cdot \frac {X_0} {c_0} \cdot \sqrt {1 - i \cdot \frac {r} {\rho_0 \cdot \omega}}}</math> Dabei ist <math>\rho_0</math> die Dichte des gasförmigen Schallmediums (bei trockener Luft bei 20°&nbsp;C ist <math>\rho_0 = 1.2 {\text{kg} \over \text{m}^3}</math>) und <math>r</math> der spezifische Strömungswiderstand des porösen Absorbermaterials in Newtonsekunden pro Biquadratmeter. Je größer der spezifische Strömungswiderstand, desto stärker ist die Schalldämpfung. Er hängt im Wesentlichen vom Material, von der Faserfeinheit und von der Massendichte <math>\rho</math>, also von der Packungsdichte, des Absorbers und vom Füllgrad in der Transmissionline ab. Weiterhin hängt er von der gleichmäßigen Verteilung des Absorbers aber auch von Faserrichtung und der Schallfrequenz ab, so dass in der folgenden Tabelle nur ungefähre Richtwerte für einige poröse, faserige Absorber angeben sind: {| class="wikitable zebra" | style="text-align:center" !Material !Massendichte !Spezifischer<br/>Strömungswiderstand !Gewichtseffizienz |- | |<math>\rho</math> in <math>\frac {\text{kg}} {\text{m}^3}</math> |<math>r</math> in <math>\frac {\text{Ns}} {\text{m}^4}</math> |<math>\eta_m = \frac {r} {\rho}</math> in <math>\frac {\text{Ns}} {\text{kg m}} = \text{Hz}</math> |- |Schafwolle |5 |4000 |800 |- |Schafwolle |10 |8000 |800 |- |Baumwolle |5 |1000 |200 |- |Baumwolle |10 |4000 |400 |- |Glaswolle |50 |200 |4 |- |Glasfaserplatten |20 |1000 |50 |- |Mineralwolle |10 |600 |60 |- |Mineralwolle |50 |10000 |200 |- |Aluminiumwolle |35 |500 |14,3 |- |Aluminiumwolle |70 |4000 |57,1 |} Die Gewichtseffizienz des Absorbermaterials <math>\eta_m</math> ist in der Tabelle als Verhältnis der spezifischen Strömungswiderstands <math>r</math> zur Massendichte <math>\rho</math> angegeben: :<math>\eta_m = \frac {r} {\rho}</math> [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Laengen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Längen.]] [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Daempfungen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Dämpfungen.]] Für die gesamte Transmissionline-Lautsprecherbox ergibt sich an der Front der Lautsprecherbox mit Dämpfung die folgende frequenzabhängige Amplitude: :<math>A_{ges,TL,D} = A_v + A_{h,TL,D} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {X_0 \over c_0} \cdot \sqrt{1 - i \cdot {r \over {\rho_0 \cdot \omega}}}}\right)</math> Der Amplitudenverlauf ergibt sich aus dem Betrag der komplexen Funktion <math>A_{ges,TL,D}</math>. Dieser muss entsprechend den Parametern Gesamtgüte des Tieftöners <math>Q</math>, Länge der Transmissionline <math>X_0</math> und spezifischer Strömungswiderstand <math>r</math> so angepasst werden, dass bei den tiefen Frequenzen ein möglichst gleichmäßiger Amplitudenverlauf entsteht. Die beiden nebenstehenden Abbildungen sollen den Einfluss der beiden Parameter <math>X_0</math> und <math>r</math> bei vorgegebenem <math>Q</math> verdeutlichen. Die als geeignet bestimmten Parameter können beim Bau einer Transmissionline-Lautsprecherbox umgesetzt werden. Insbesondere die richtige Bedämpfung muss hierbei jedoch experimentell überprüft und gegebenenfalls korrigiert werden. Ferner muss bei der Ansteuerung von Tief- und Mitteltöner die aus obiger Gleichung resultierende Amplitude :<math>|A_{ges,TL,D}|</math> und Phasendifferenz :<math>arg(A_{ges,TL,D})</math> bei der Übergangsfrequenz zwischen dem System aus Transmissionline und Tieftöner auf der einen Seite und Mitteltöner auf der anderen Seite mittels der Frequenzweiche ausgeglichen werden. <div style="clear:both"></div> === Erfahrungswerte für den Entwurf einer Transmissionline-Lautsprecherbox === [[Datei:Transmissionline-Lautsprecher.P1117437.jpg|mini|hochkant=1.5|Transmissionline-Lautsprecherbox aus dem Jahr 1986 mit trapezförmiger Grundfläche, oben der Tieftöner, darunter der Mitteltöner und unten der Hochtöner. Die beiden Öffnungen der unten im Gehäuse symmetrisch nach rechts und links aufgeteilten akustischen Transmissionlines befinden sich oben an den abgeschrägten Seiten hinter dem Tieftöner. Rechnerisch wird im Frequenzgang beim Unterschreiten von 30&nbsp;Hertz ein Pegelabfall von drei Dezibel erreicht. In einem größeren Abhöraum betrug die Pegelschwankung der Kombination aus Tieftöner und Transmissionline zwischen 20&nbsp;und 50&nbsp;Hertz ungefähr ein Dezibel.]] Der Querschnitt der Transmissionline direkt hinter dem Lautsprecher sollte mindestens so groß sein wie die Lautsprechermembranfläche oder sogar etwas darüber liegen. Entlang der Transmissionline kann sich deren Querschnitt um maximal 25&nbsp;Prozent verringern. Für den Transmissionline-Betrieb geeignete Tieftöner sollten eine relativ hohe Gesamtgüte <math>\left(Q \lessapprox 1\right)</math> haben. Mit einer passiven Frequenzweiche, über aktive Frequenzfilter beziehungsweise mit Hilfe von digitalen Signalprozessoren werden die elektrischen Signale auf die Tief- und Mittel- sowie gegebenenfalls auch die Hochtöner verteilt. Um unerwünschte Effekte durch akustische Interferenzen zu minimieren, sollten die seitlichen Abstände zwischen Mittel- und Tieftöner sowie zwischen Hoch- und Mitteltöner so gering wie möglich gehalten werden. Außerdem sollten alle Lautsprecher bündig mit dem Gehäuse abschließen und in einer Ebene liegen. Die Austrittsöffnung einer Transmissionline kann konstruktiv wie ein Subtieftöner betrachtet werden. Der Wert des spezifischen Strömungswiderstands <math>r</math> (und somit der Dichte <math>\rho_0</math>) sollte bei der fertiggestellten Lautsprecherbox experimentell überprüft (zum Beispiel mit Frequenzgenerator oder mit Rauschgenerator und Frequenzanalysator) und gegebenenfalls angepasst werden, da er für einen in die Transmissionline eingebauten Absorber meist nicht genau genug berechnet werden kann. Der Mess- oder Abhörraum hat durch seine Eigenfrequenzen im tiefen Frequenzbereich einen deutlichen Einfluss auf die Tieftonwiedergabe. Insbesondere bei parallelen Wänden (respektive Böden und Decken) können stehende Wellen Raumresonanzen hervorrufen, wenn die Schallwellenlänge <math>\lambda</math> dem Doppelten einer Raumdimension mit der Länge <math>s</math> entspricht: :<math>f_{Resonanz} = \frac {c_0} {\lambda} = \frac {c_0} {2 \cdot s}</math> In einem quaderförmigen Raum mit der Länge <math>l</math>, der Breite <math>b</math> und der Höhe <math>h</math> ergibt sich nach dem Satz der Pythagoras die kleinste Resonanzfrequenz aus der Raumdiagonale <math>d</math>: :<math>d = \sqrt {l^2 + b^2 + h^2}</math> :<math>f_{Resonanz, min} = \frac {c_0} {2 \cdot d} = \frac {c_0} {2 \cdot \sqrt {l^2 + b^2 + h^2}}</math> Ein quaderförmiger Raum mit einer quadratischen Grundfläche von 100&nbsp;Quadratmetern hat demnach eine niedrigste Eigenfrequenz von ungefähr 12&nbsp;Hertz. Durch schwere Vorhänge und Teppiche oder andere Schallabsorber kann die Schallreflexion in einem Raum deutlich reduziert werden, wobei die Schallabsorption für tiefer werdende Frequenzen in der Regel zunehmend geringer wird. Im freien Feld beziehungsweise in schallreflexionsarmen Messräumen entfallen solche Raumreflexionen oder sind so stark reduziert, so dass sich für einen gegebenen Lautsprecher ein anderes Klangbild als in einem gewöhnlichen Raum ergibt. {| class="wikitable" |+ Parameter der abgebildeten Transmissionline-Lautsprecherbox ! Parameter !! Formelsymbol !! Wert |- | Eigenresonanzfrequenz des Tieftöners || <math>f_0</math> || <math>\text {31 Hz}</math> |- | Güte des Tieftöners || <math>Q</math> || <math>\text {0,65}</math> |- | Länge der Transmissionline || <math>X_0</math> || <math>\text {2,75 m}</math> |- | Mittlerer spezifischer Widerstand der Dämmung mit Mineralwolle || <math>r</math> || <math>\text {1500 } \frac {\text {Ns}} {\text {m}^4}</math> |- | Mittlere Massendichte der Dämmung mit Mineralwolle || <math>\rho</math> || <math>\text {17,4 } \frac {\text {kg}} {\text {m}^3}</math> |- | Gewichtseffizienz der Dämmung mit Mineralwolle || <math>\eta_m</math> || <math>\text {86 } \text {Hz}</math> |} ==Widmung== Diese Zusammenstellung ist meinem Studienfreund '''Dr.&nbsp;Lutz König''' gewidmet. Der Hauptautor dankt ihm für seine freundschaftlichen, umfangreichen und stets förderlichen Beiträge zur Realisierung unserer 1986 in Eigenentwicklung hergestellten vier Lautsprecherboxen. == Literatur == * Arthur R.&nbsp;Bailey: ''A Non-resonant Loudspeaker Enclosure Design'' - ''Using acoustic transmission line with low-pass filter characteristics'', Wireless World, October 1965, p.&nbsp;483-486 * Arthur R.&nbsp;Bailey: ''The Transmission-line Loudspeaker Enclosure'' - ''A re-examination of the general principle and a suggested new method of construction'', Wireless World, May 1972, p.&nbsp;215-217 * {{w|Ludwig Bergmann (Physiker)|Ludwig Bergmann}}, {{w|Clemens Schaefer (Physiker)|Clemens Schäfer}}: ''{{w|Bergmann-Schaefer_Lehrbuch_der_Experimentalphysik|Lehrbuch der Experimentalphysik}}'', Band 1: ''Mechanik, Akustik, Wärme'', 9. Auflage, de Gruyter, 1974, ISBN 978-3-1100-4861-2 * Fridolin P.&nbsp;Mechel: ''Schallabsorption'', Kapitel&nbsp;18, in: {{w|Manfred Heckl}}, {{w|Helmut A. Müller (Akustiker)|Helmut A.&nbsp;Müller}}: ''Taschenbuch der technischen Akustik'', Springer-Verlag, Berlin, Heidelberg, New York, 1975, ISBN 3-6429-7357-4 * Hans Herbert Klinger: ''Lautsprecher und Lautsprechergehäuse für HiFi'', Franzis-Verlag GmbH, München, 1981, ISBN 3-7723-1051-6 * Heinz Sahm: ''HiFi-Lautsprecher'', ''Grundlagen der elektrodynamischen Lautsprecher in unendlicher Schallwand und im Gehäuse'', Franzis-Verlag GmbH, München, 1982, ISBN 3-7723-6522-1 * Berndt Stark: ''Lautsprecher-Handbuch'' - ''Theorie und Praxis des Boxenbauens'', Richard Pflaum Verlag, München, 1985, ISBN 3-7905-0433-5 == Siehe auch == * [[Grundlegendes zur Akustik]] == Weblinks == {{Commonscat|Transmission line loudspeakers}} == Zusammenfassung des Projekts == {{Vorlage:StatusBuch|10}} * '''Zielgruppe und Lernziele:''' Dieses Buch richtet sich an akustisch interessierte Leser mit mathematischer Ausbildung. Es beschreibt einen vollständigen Weg zur Berechnung einer bedämpften akustischen Transmissionline mit komplexwertigen Funktionen. Aufbauend auf grundlegenden Betrachtungen zur Schallerzeugung und -abstrahlung von Lautsprechern wird durch schrittweise Erweiterungen und Ergänzungen die Funktionsweise einer solchen Transmissionline erarbeitet. * '''Ansprechperson:''' Ansprechpartner ist [[Benutzer:Bautsch]] [[Kategorie:Geometrische Kuriositäten‎]] cfbe5rehifebybh78lloqm86i4pgg39 1088124 1088123 2026-06-14T00:35:37Z Bautsch 35687 /* Quantitative Berechnung */ dimensionslose 1088124 wikitext text/x-wiki {{Regal | ort=Physik}} {{Infoleiste|Physik|Studium|S|Technik}} [[Bild:Transmission-line.png|rechts|mini|Beispiel für eine Transmissionline als akustischen Kanal (blaue Pfeile) für den rückwärtigen Schall eines Lautsprechers (orangefarben) in einer Lautsprecherbox (dunkelbraun).]] == Einführung == '''Transmissionline''' ("Übertragungsleitung") nennt man in der Lautsprecherakustik einen in der Regel mehrfach gefalteten Kanal, durch den der von einem Lautsprecher nach hinten abgegebene Schall hindurchtritt. Am Ende dieses Kanals befindet sich eine Öffnung, durch die der Schall heraustreten kann. Dieser Schall kann unter geeigneten Voraussetzungen dazu verwendet werden, den Amplituden-Frequenzgang einer elektrodynamischen Lautsprecherbox am tieffrequenten Ende zu erweitern und zu verbessern. Ausgehend von idealisierten theoretischen Ansätzen zum Abstrahlverhalten von Lautsprechern und mit der Theorie der quasihomogenen Absorber wird in diesem Buch darauf eingegangen, wie der Tieftonbereich einer Transmissionline-Lautsprecherbox dimensioniert und akustisch gedämpft werden kann. Dazu werden wegen des einfachen mathematischen Formalismus Funktionen komplexer Zahlen ({{w|Eulersche Formel}}) herangezogen. Dieses Wikibook basiert auf der privaten Vorveröffentlichung ''Anleitung zur akustischen Berechnung einer elektrodynamischen "transmission line"-Lautsprecherbox'' aus dem Jahr 2000. Das hier vorgestellte Berechnungsverfahren wurde vom Autor im Jahr 1986 entwickelt und für den Bau von Transmissionline-Lautsprecherboxen angewandt. <div style="clear:both"></div> == Abstrahlverhalten von Lautsprechern == [[Bild:Amplitudenfrequenzgang.Lautsprecher.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang eines elektrodynamischen Lautsprechers.]] Der Amplitudenfrequenzgang <math>A_0</math> eines elektrodynamischen Lautsprechers mit der Gesamtgüte <math>Q</math> und der Eigenfrequenz <math>f_0</math> berechnet sich über der Schallfrequenz <math>f</math> theoretisch zu :<math>A_0 (f) = \frac {\frac {f} {f_0}} {\sqrt {\frac {1} {Q^2} + \left(\frac {f} {f_0} - \frac {f_0} {f}\right)^2}}</math> Im Resonanzfall <math>f = f_0</math> vereinfacht sich diese Gleichung zu :<math>A_0 (f_0) = Q</math> Mit zunehmender Frequenz fällt der Frequenzgang jedoch entgegen der Aussage obiger Gleichung wieder ab, da die Lautsprechermembran wegen ihrer mechanischen Trägheit nicht mehr mitschwingen kann. Weitere Abweichungen ergeben sich in der Praxis, weil auch andere Eigenfrequenzen als <math>f_0</math> noch kleine Beiträge zum Frequenzgang leisten. Diese Eigenfrequenzen werden im Allgemeinen durch ungewollte Schwingungsmoden der Lautsprechermembran verursacht. <div style="clear:both"></div> === Freier Lautsprecher === [[Bild:Freier.Lautsprecher.gif|upright=2|miniatur|rechts|Freier Lautsprecher.]] Wird ein Lautsprecher bei der Frequenz <math>f</math> ohne Schallwand oder Gehäuse betrieben, so interferieren an der Stelle <math>x = 0</math> (also an der Lautsprecheroberfläche) über der Zeit <math>t</math> die nach vorne abgegebene Schallwelle :<math>A_v = A(f,t) = A_0(f) \cdot e^{-i \omega t}</math> und die nach hinten abgestrahlte Schallwelle :<math>A_h = -A(f,t) = -A_0(f) \cdot e^{-i \omega t}</math> mit der {{w|Eulersche Zahl|Eulerschen Zahl}} <math>e</math>, der {{w|Imaginäre Zahl|imaginären Einheit}} <math>i = \sqrt {-1}</math> und der {{w|Kreisfrequenz}} <math>\omega = 2 \pi f</math> zu allen Zeitpunkten destruktiv: :<math>A_{ges} = A_v + A_h = 0</math> Dies bedeutet, dass die gesamte Amplitudenfrequenzgang <math>A_{ges}</math> null wird, da sich die beiden Schallwellen gegenseitig auslöschen. Dies wird auch als {{w|Akustischer Kurzschluss|akustischer Kurzschluss}} bezeichnet. <div style="clear:both"></div> === Lautsprecher mit Schallwand === [[Bild:Lautsprecher.mit.Schallwand.gif|upright=2|miniatur|rechts|Lautsprecher mit unendlich ausgedehnter Schallwand.]] Die Probleme beim freien Lautsprecher können umgangen werden, wenn der Lautsprecher in eine im Verhältnis zur größten abzustrahlenden Schallwellenlänge möglichst weit ausgedehnte Schallwand eingebaut wird. Allerdings muss eine solche Schallwand meist sehr große Abmessungen haben. Bei einer minimalen Frequenz von beispielsweise 30&nbsp;Hertz müsste die Schallwand seitlich mehrere Meter Ausdehnung haben. :<math>A_{ges} = A_v = A(f,t) = A_0(f)\cdot e^{-i \omega t}</math> wobei <math>A_v</math> die nach vorne abgestrahlte Schallwelle repräsentiert, die in diesem Fall mit der gesamten abgestrahlten <math>A_{ges}</math> identisch ist. <div style="clear:both"></div> === Schallabyrinth === [[Bild: Schallabyrinth.gif|upright=2|miniatur|rechts|Schalllabyrinth.]] Eine Alternative wäre der Einbau des Lautsprechers in ein sogenanntes '''Schallabyrinth'''. Die Schallenergie der nach hinten abgegebenen Schallwelle wird hier durch Mehrfachreflexion und Mehrfachstreuung in Verbindung mit Absorption vernichtet. Auch ein solches Schallabyrinth muss jedoch sehr groß dimensioniert werden, damit der Schall weitgehend absorbiert werden kann. Wie auch bei der Schallwand besteht der Nachteil, dass die vom Lautsprecher nach hinten abgestrahlte Schallenergie nicht genutzt wird. <div style="clear:both"></div> == Geschlossene Lautsprecherbox == [[Bild:Geschlossene.Lautsprecherbox.gif|upright=2|miniatur|rechts|Geschlossene Lautsprecherbox.]] Das ideale Verhalten eines Schalllabyrinths wird bei '''Lautsprecherboxen in geschlossener Bauweise''' nicht erreicht. Neben der nach vorne abgestrahlten Schallwelle <math>A_v</math> können auch die nach hinten abgestrahlten Schallwellen <math>A_h</math>, die an den Gehäuseinnenwänden reflektiert werden, durch die Lautsprecheröffnung hinaustreten. Als Folge der Reflexionen treten ferner stehende Wellen auf, die sich negativ auf den Amplituden- und Phasenfrequenzgang der Lautsprecherbox auswirken. Dies kann zwar eingeschränkt werden, indem das Innere der Lautsprecherbox mit einem schallschluckenden Medium gefüllt wird und die Gehäusewände nicht parallel ausgerichtet sind, jedoch lässt sich dieser Effekt nie vollständig vermeiden. Ferner kann besonders bei niedrigen Frequenzen ein so hoher Schalldruck auftreten, dass eine Rückkopplung auf die Lautsprechermembran stattfindet, die sich ebenso wie die reflektierten Schallwellen als nichtlinearer Effekt negativ bemerkbar macht. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich aus der Überlagerung aller Schallwellen: :<math>A_{ges} = A_v + B(A_h) + C(A_h)</math>, wobei die Funktion <math>B(A_h)</math> die im Lautsprechergehäuse gedämpften und reflektieren Schallwellen repräsentiert und <math>C(A_h)</math> die nichtlinearen Anteile durch Rückkopplung des Schalls. <div style="clear:both"></div> === Teilventilierte Lautsprecherbox === [[Bild:Teilventilierte.Lautsprecherbox.gif|upright=2|miniatur|rechts|Teilventilierte Lautsprecherbox.]] Der hohe Schalldruck wird bei den sogenannten '''teilventilierten Lautsprecherboxen''' durch eine kleine Öffnung vermieden, die zum Druckausgleich dient. Wenn solche Lautsprecherboxen gut bedämpft sind und keine parallelen Gehäusewände haben, damit sich keine stehenden Wellen aufbauen können, stellen sie einen recht guten Kompromiss dar, die nach hinten abgestrahlte Schallenergie findet aber auch hier keine Verwendung bei der Klangwiedergabe. <div style="clear:both"></div> === Bass-Reflex-Lautsprecherbox === [[Bild:Bass.Reflex.Lautsprecherbox.gif|upright=2|miniatur|rechts|Bass-Reflex-Lautsprecherbox.]] Bei '''Bass-Reflex-Lautsprecherboxen''' wird die Eigenfrequenz des Lautsprechergehäuses bewusst eingesetzt, um die Schallabstrahlung der Lautsprecherbox meist im Tieftonbereich zu verstärken. Die Lautsprecherbox hat eine zusätzliche, meist nach vorn gerichtete Rohröffnung und wirkt somit als Helmholtz-Resonator mit der Amplitudenfunktion <math>B_v(f,t)</math>. Es ist auf diese Weise jedoch problematisch, einen gleichmäßigen Frequenzgang zu erreichen, da in der Regel eine zu starke Verstärkung in einem zu schmalen Frequenzband besteht und dadurch die beiden nach vorne abgestrahlten Schallwellen vom Lautsprecher <math>A_v</math> und von der zusätzlichen Öffnung <math>B_v</math> sehr schlecht aufeinander abgestimmt werden können. Auch bei Bass-Reflex-Lautsprecherboxen kann es wie bei geschlossenen Lautsprecherboxen natürlich zusätzlich noch zu stehenden Wellen kommen. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich also im Wesentlichen aus der Überlagerung zweier Schallwellen: :<math>A_{ges} = A_v + B_v</math>, wobei die Funktion <math>A_v</math> die nach vorne abgestrahlte Schallwelle und <math>B_v</math> die aus der Bass-Reflex-Öffnung abgestrahlten Schallwellen repräsentiert. <div style="clear:both"></div> == Transmissionline-Lautsprecherbox == === Transmissionline ohne Dämpfung === [[Bild:Transmissionline.Lautsprecherbox.ohne.Daempfung.gif|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox ohne Dämpfung.]] Die vom Lautsprecher nach hinten abgegebene Schallwelle <math>A_h</math> kann durch einen Schallkanal mit der Länge <math>X_0</math> geleitet werden, an dessen Ende sich eine Öffnung befindet. Dieser idealerweise resonanzfreie Schallkanal wurde von dem an der Universität von Bradford lehrenden Briten Arthur R.&nbsp;Bailey '''Transmissionline''' genannt. Die aus dieser Öffnung heraustretende Schallwelle <math>A_{h,TL}</math> kann mit der vom Lautsprecher nach vorn abgestrahlten Schallwelle <math>A_v</math> interferieren. Die Amplitude der interferierten Schallwelle ergibt sich im Falle des ungedämpften Schallkanals aus der Summe der beiden Anteile: :<math>A_v = A_0(f)</math> wie beim freien Lautsprecher und :<math>A_{h,TL} = -A_0(f)\cdot e^{-i \omega \cdot {X_0 \over c_0}}</math> Hierbei ist <math>c_0</math> die Schallgeschwindigkeit im gasförmigen Schallmedium (in Luft bei 20°&nbsp;C und 60% relativer Luftfeuchtigkeit ist <math>c_0 = 344 {\frac {m} {s}}</math>). An der Lautsprecherfront ergibt sich für <math>A_{ges,TL}</math> ohne Dämpfung also: :<math>A_{ges,TL} = A_v + A_{h,TL} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {\frac {X_0} {c_0}}}\right)</math> <div style="clear:both"></div> === Transmissionline mit Dämpfung === ==== Qualitative Beschreibung ==== In den 1960er Jahren kam Bailey auf die Idee, die von einem Tieftöner nach hinten abgestrahlte Schallenergie in einem hinreichend langen, bedämpften und am Ende offenen Schallkanal quasi "totlaufen" zu lassen, was er in zwei Artikeln im Fachmagazin Wireless World beschrieben hat. Die Dämpfung ist dabei ein integraler Bestandteil der Konstruktion von Bailey. Die Dämpfung, die Schallgeschwindigkeit und somit auch die Wellenlänge der Schallwellen sind bei einem solchen Tiefpass frequenzabhängig (Dispersion). Die Schallwellen werden umso weniger stark gedämpft, je geringer die Frequenz beziehungsweise je größer die Wellenlänge der Schallwellen ist. Die höherfrequenten Anteile werden durch die akustische Dämpfung in Wärmeenergie umgewandelt, und die sehr tieffrequenten am Ende aus der Transmissionline noch austretenden Schallwellen können bei geeigneter Interferenz mit den direkten, nach vorne abgegebenen Schallwellen des eingesetzten Tieftöners den Amplitudenfrequenzgang bei tiefen Frequenzen verbessern. Die ersten nach diesem Prinzip gebauten kommerziellen Lautsprecherboxen wurden zunächst von der in Cambridgeshire ansässigen und mit Bailey kooperierenden Firma Radford und später unter anderem von IMF Electronics und in Deutschland von {{w|Lautsprecher Teufel}} auf den Markt gebracht. Die vom Lautsprecher nach hinten abgegebene Schallwelle wird in dem zum Beispiel mit langfaseriger Schafwolle gedämmten Schallkanal zunehmend abgeschwächt, sehr tiefe Frequenzen können den Schallkanal jedoch nur schwach gedämpft verlassen und zur Schallwiedergabe verwendet werden. Die Länge des Kanals sollte nach Bailey mindestens etwa ein Viertel der Wellenlänge der unteren Grenzfrequenz betragen, kann aber auch länger sein. Er kann in verschiedener Weise geknickt oder gefaltet werden, um in einem konventionellen Gehäuse Platz zu finden, jedoch sollten Reflexionen (zum Beispiel mit gekrümmten oder abgeschrägten Flächen) vermieden werden. Da bei diesem "nicht-resonanten Lautsprechergehäuse" (Bailey) ein großer Teil der nach hinten abgestrahlte Schallenergie vernichtet wird, ist der Wirkungsgrad von Transmissionline-Lautsprechern relativ schlecht, was jedoch dank leistungsfähiger (Transistor-)Verstärker normalerweise keine Rolle spielt. Bei korrekter Konstruktion zeichnen sich Transmissionline-Lautsprecher durch sehr gute, klangneutrale (Tief-)Basswiedergabe und sehr gutes Impulsverhalten aus. Wegen Größe, Gewicht und der relativ hohen Fertigungskosten konnten sie sich jedoch auf lange Sicht nicht auf dem Markt durchsetzen. <gallery caption="Tieffrequente Sinustöne mit 30 Sekunden Dauer" perrow="5"> Sine Wave 10Hz 30s.ogg|10 Hertz Sine Wave 20Hz 30s.ogg|20 Hertz Sine Wave 30Hz 30s.ogg|30 Hertz Sine Wave 40Hz 30s.ogg|40 Hertz Sine Wave 50Hz 30s.ogg|50 Hertz </gallery> ==== Quantitative Berechnung ==== [[Bild:Transmissionline.Lautsprecherbox.mit.Daempfung.png|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox mit Dämpfung.]] Im folgenden werden die von Bailey im Wesentlichen nur qualitativ beschriebenen Sachverhalte anhand entsprechender akustischer Formeln quantitativ beschrieben. Wird die Transmissionline mit einem porösen, faserigen Absorber bedämpft, so kann die aus der Transmissionline mit der Länge <math>X_0</math> austretende Schallwellenamplitude <math>A_{h,TL,D}</math> nach Fridolin Peter Mechel nach der '''Theorie der quasihomogenen Absorber''' mit der komplexwertigen Ausbreitungskonstante <math>\Gamma</math> näherungsweise angegeben werden. Die dimensionslose Hilfsgröße <math>\Omega</math> steht hierbei für das Verhältnis des Trägheitswiderstands der Luft <math>t = \rho_0 \cdot \omega</math> zum spezifischen Strömungswiderstand des porösen Absorbermaterials <math>r</math>, was ähnlich wie eine Reynolds-Zahl interpretiert werden kann: :<math>\Omega = \frac {t} {r} = \frac {\rho_0 \cdot \omega} {r}</math> :<math>\Gamma = \frac {i \omega} {c_0} \cdot \sqrt {1 - \frac {i} {\Omega}} = \frac {i \omega} {c_0} \cdot \sqrt {1 - i \cdot \frac {r} {\rho_0 \cdot \omega}}</math> :<math>A_{h,TL,D} = -A_0 (f) \cdot e^ {- \Gamma \cdot X_0} = -A_0 (f) \cdot e^ {-i \omega \cdot \frac {X_0} {c_0} \cdot \sqrt {1 - i \cdot \frac {r} {\rho_0 \cdot \omega}}}</math> Dabei ist <math>\rho_0</math> die Dichte des gasförmigen Schallmediums (bei trockener Luft bei 20°&nbsp;C ist <math>\rho_0 = 1.2 {\text{kg} \over \text{m}^3}</math>). Je größer der spezifische Strömungswiderstand, desto stärker ist die Schalldämpfung. Er hängt im Wesentlichen vom Material, von der Faserfeinheit und von der Massendichte <math>\rho</math>, also von der Packungsdichte, des Absorbers und vom Füllgrad in der Transmissionline ab. Weiterhin hängt er von der gleichmäßigen Verteilung des Absorbers aber auch von Faserrichtung und der Schallfrequenz ab, so dass in der folgenden Tabelle nur ungefähre Richtwerte für einige poröse, faserige Absorber angeben sind: {| class="wikitable zebra" | style="text-align:center" !Material !Massendichte !Spezifischer<br/>Strömungswiderstand !Gewichtseffizienz |- | |<math>\rho</math> in <math>\frac {\text{kg}} {\text{m}^3}</math> |<math>r</math> in <math>\frac {\text{Ns}} {\text{m}^4}</math> |<math>\eta_m = \frac {r} {\rho}</math> in <math>\frac {\text{Ns}} {\text{kg m}} = \text{Hz}</math> |- |Schafwolle |5 |4000 |800 |- |Schafwolle |10 |8000 |800 |- |Baumwolle |5 |1000 |200 |- |Baumwolle |10 |4000 |400 |- |Glaswolle |50 |200 |4 |- |Glasfaserplatten |20 |1000 |50 |- |Mineralwolle |10 |600 |60 |- |Mineralwolle |50 |10000 |200 |- |Aluminiumwolle |35 |500 |14,3 |- |Aluminiumwolle |70 |4000 |57,1 |} Die Gewichtseffizienz des Absorbermaterials <math>\eta_m</math> ist in der Tabelle als das Verhältnis des spezifischen Strömungswiderstands <math>r</math> zur Massendichte <math>\rho</math> angegeben: :<math>\eta_m = \frac {r} {\rho}</math> [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Laengen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Längen.]] [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Daempfungen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Dämpfungen.]] Für die gesamte Transmissionline-Lautsprecherbox ergibt sich an der Front der Lautsprecherbox mit Dämpfung durch die Überlagerung des direkten Schalls mit dem Schall aus der gedämpften Transmissionline die folgende frequenzabhängige Amplitude: :<math>A_{ges,TL,D} = A_v + A_{h,TL,D} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {X_0 \over c_0} \cdot \sqrt{1 - i \cdot {r \over {\rho_0 \cdot \omega}}}}\right)</math> Der Amplitudenverlauf ergibt sich aus dem Betrag der komplexen Funktion <math>A_{ges,TL,D}</math>. Dieser muss entsprechend den Parametern Gesamtgüte des Tieftöners <math>Q</math>, Länge der Transmissionline <math>X_0</math> und spezifischer Strömungswiderstand <math>r</math> so angepasst werden, dass bei den tiefen Frequenzen ein möglichst gleichmäßiger Amplitudenverlauf entsteht. Die beiden nebenstehenden Abbildungen sollen den Einfluss der beiden Parameter <math>X_0</math> und <math>r</math> bei vorgegebenem <math>Q</math> verdeutlichen. Die als geeignet bestimmten Parameter können beim Bau einer Transmissionline-Lautsprecherbox umgesetzt werden. Insbesondere die richtige Bedämpfung muss hierbei jedoch experimentell überprüft und gegebenenfalls korrigiert werden. Ferner muss bei der Ansteuerung von Tief- und Mitteltöner die aus obiger Gleichung resultierende Amplitude :<math>|A_{ges,TL,D}|</math> und Phasendifferenz :<math>arg(A_{ges,TL,D})</math> bei der Übergangsfrequenz zwischen dem System aus Transmissionline und Tieftöner auf der einen Seite und Mitteltöner auf der anderen Seite mittels der Frequenzweiche ausgeglichen werden. <div style="clear:both"></div> === Erfahrungswerte für den Entwurf einer Transmissionline-Lautsprecherbox === [[Datei:Transmissionline-Lautsprecher.P1117437.jpg|mini|hochkant=1.5|Transmissionline-Lautsprecherbox aus dem Jahr 1986 mit trapezförmiger Grundfläche, oben der Tieftöner, darunter der Mitteltöner und unten der Hochtöner. Die beiden Öffnungen der unten im Gehäuse symmetrisch nach rechts und links aufgeteilten akustischen Transmissionlines befinden sich oben an den abgeschrägten Seiten hinter dem Tieftöner. Rechnerisch wird im Frequenzgang beim Unterschreiten von 30&nbsp;Hertz ein Pegelabfall von drei Dezibel erreicht. In einem größeren Abhöraum betrug die Pegelschwankung der Kombination aus Tieftöner und Transmissionline zwischen 20&nbsp;und 50&nbsp;Hertz ungefähr ein Dezibel.]] Der Querschnitt der Transmissionline direkt hinter dem Lautsprecher sollte mindestens so groß sein wie die Lautsprechermembranfläche oder sogar etwas darüber liegen. Entlang der Transmissionline kann sich deren Querschnitt um maximal 25&nbsp;Prozent verringern. Für den Transmissionline-Betrieb geeignete Tieftöner sollten eine relativ hohe Gesamtgüte <math>\left(Q \lessapprox 1\right)</math> haben. Mit einer passiven Frequenzweiche, über aktive Frequenzfilter beziehungsweise mit Hilfe von digitalen Signalprozessoren werden die elektrischen Signale auf die Tief- und Mittel- sowie gegebenenfalls auch die Hochtöner verteilt. Um unerwünschte Effekte durch akustische Interferenzen zu minimieren, sollten die seitlichen Abstände zwischen Mittel- und Tieftöner sowie zwischen Hoch- und Mitteltöner so gering wie möglich gehalten werden. Außerdem sollten alle Lautsprecher bündig mit dem Gehäuse abschließen und in einer Ebene liegen. Die Austrittsöffnung einer Transmissionline kann konstruktiv wie ein Subtieftöner betrachtet werden. Der Wert des spezifischen Strömungswiderstands <math>r</math> (und somit der Dichte <math>\rho_0</math>) sollte bei der fertiggestellten Lautsprecherbox experimentell überprüft (zum Beispiel mit Frequenzgenerator oder mit Rauschgenerator und Frequenzanalysator) und gegebenenfalls angepasst werden, da er für einen in die Transmissionline eingebauten Absorber meist nicht genau genug berechnet werden kann. Der Mess- oder Abhörraum hat durch seine Eigenfrequenzen im tiefen Frequenzbereich einen deutlichen Einfluss auf die Tieftonwiedergabe. Insbesondere bei parallelen Wänden (respektive Böden und Decken) können stehende Wellen Raumresonanzen hervorrufen, wenn die Schallwellenlänge <math>\lambda</math> dem Doppelten einer Raumdimension mit der Länge <math>s</math> entspricht: :<math>f_{Resonanz} = \frac {c_0} {\lambda} = \frac {c_0} {2 \cdot s}</math> In einem quaderförmigen Raum mit der Länge <math>l</math>, der Breite <math>b</math> und der Höhe <math>h</math> ergibt sich nach dem Satz der Pythagoras die kleinste Resonanzfrequenz aus der Raumdiagonale <math>d</math>: :<math>d = \sqrt {l^2 + b^2 + h^2}</math> :<math>f_{Resonanz, min} = \frac {c_0} {2 \cdot d} = \frac {c_0} {2 \cdot \sqrt {l^2 + b^2 + h^2}}</math> Ein quaderförmiger Raum mit einer quadratischen Grundfläche von 100&nbsp;Quadratmetern hat demnach eine niedrigste Eigenfrequenz von ungefähr 12&nbsp;Hertz. Durch schwere Vorhänge und Teppiche oder andere Schallabsorber kann die Schallreflexion in einem Raum deutlich reduziert werden, wobei die Schallabsorption für tiefer werdende Frequenzen in der Regel zunehmend geringer wird. Im freien Feld beziehungsweise in schallreflexionsarmen Messräumen entfallen solche Raumreflexionen oder sind so stark reduziert, so dass sich für einen gegebenen Lautsprecher ein anderes Klangbild als in einem gewöhnlichen Raum ergibt. {| class="wikitable" |+ Parameter der abgebildeten Transmissionline-Lautsprecherbox ! Parameter !! Formelsymbol !! Wert |- | Eigenresonanzfrequenz des Tieftöners || <math>f_0</math> || <math>\text {31 Hz}</math> |- | Güte des Tieftöners || <math>Q</math> || <math>\text {0,65}</math> |- | Länge der Transmissionline || <math>X_0</math> || <math>\text {2,75 m}</math> |- | Mittlerer spezifischer Widerstand der Dämmung mit Mineralwolle || <math>r</math> || <math>\text {1500 } \frac {\text {Ns}} {\text {m}^4}</math> |- | Mittlere Massendichte der Dämmung mit Mineralwolle || <math>\rho</math> || <math>\text {17,4 } \frac {\text {kg}} {\text {m}^3}</math> |- | Gewichtseffizienz der Dämmung mit Mineralwolle || <math>\eta_m</math> || <math>\text {86 } \text {Hz}</math> |} ==Widmung== Diese Zusammenstellung ist meinem Studienfreund '''Dr.&nbsp;Lutz König''' gewidmet. Der Hauptautor dankt ihm für seine freundschaftlichen, umfangreichen und stets förderlichen Beiträge zur Realisierung unserer 1986 in Eigenentwicklung hergestellten vier Lautsprecherboxen. == Literatur == * Arthur R.&nbsp;Bailey: ''A Non-resonant Loudspeaker Enclosure Design'' - ''Using acoustic transmission line with low-pass filter characteristics'', Wireless World, October 1965, p.&nbsp;483-486 * Arthur R.&nbsp;Bailey: ''The Transmission-line Loudspeaker Enclosure'' - ''A re-examination of the general principle and a suggested new method of construction'', Wireless World, May 1972, p.&nbsp;215-217 * {{w|Ludwig Bergmann (Physiker)|Ludwig Bergmann}}, {{w|Clemens Schaefer (Physiker)|Clemens Schäfer}}: ''{{w|Bergmann-Schaefer_Lehrbuch_der_Experimentalphysik|Lehrbuch der Experimentalphysik}}'', Band 1: ''Mechanik, Akustik, Wärme'', 9. Auflage, de Gruyter, 1974, ISBN 978-3-1100-4861-2 * Fridolin P.&nbsp;Mechel: ''Schallabsorption'', Kapitel&nbsp;18, in: {{w|Manfred Heckl}}, {{w|Helmut A. Müller (Akustiker)|Helmut A.&nbsp;Müller}}: ''Taschenbuch der technischen Akustik'', Springer-Verlag, Berlin, Heidelberg, New York, 1975, ISBN 3-6429-7357-4 * Hans Herbert Klinger: ''Lautsprecher und Lautsprechergehäuse für HiFi'', Franzis-Verlag GmbH, München, 1981, ISBN 3-7723-1051-6 * Heinz Sahm: ''HiFi-Lautsprecher'', ''Grundlagen der elektrodynamischen Lautsprecher in unendlicher Schallwand und im Gehäuse'', Franzis-Verlag GmbH, München, 1982, ISBN 3-7723-6522-1 * Berndt Stark: ''Lautsprecher-Handbuch'' - ''Theorie und Praxis des Boxenbauens'', Richard Pflaum Verlag, München, 1985, ISBN 3-7905-0433-5 == Siehe auch == * [[Grundlegendes zur Akustik]] == Weblinks == {{Commonscat|Transmission line loudspeakers}} == Zusammenfassung des Projekts == {{Vorlage:StatusBuch|10}} * '''Zielgruppe und Lernziele:''' Dieses Buch richtet sich an akustisch interessierte Leser mit mathematischer Ausbildung. Es beschreibt einen vollständigen Weg zur Berechnung einer bedämpften akustischen Transmissionline mit komplexwertigen Funktionen. Aufbauend auf grundlegenden Betrachtungen zur Schallerzeugung und -abstrahlung von Lautsprechern wird durch schrittweise Erweiterungen und Ergänzungen die Funktionsweise einer solchen Transmissionline erarbeitet. * '''Ansprechperson:''' Ansprechpartner ist [[Benutzer:Bautsch]] [[Kategorie:Geometrische Kuriositäten‎]] noaj1ygj7d1osa0jyib6wtjnly6x9au 1088125 1088124 2026-06-14T00:43:53Z Bautsch 35687 /* Erfahrungswerte für den Entwurf einer Transmissionline-Lautsprecherbox */ Zentimeter 1088125 wikitext text/x-wiki {{Regal | ort=Physik}} {{Infoleiste|Physik|Studium|S|Technik}} [[Bild:Transmission-line.png|rechts|mini|Beispiel für eine Transmissionline als akustischen Kanal (blaue Pfeile) für den rückwärtigen Schall eines Lautsprechers (orangefarben) in einer Lautsprecherbox (dunkelbraun).]] == Einführung == '''Transmissionline''' ("Übertragungsleitung") nennt man in der Lautsprecherakustik einen in der Regel mehrfach gefalteten Kanal, durch den der von einem Lautsprecher nach hinten abgegebene Schall hindurchtritt. Am Ende dieses Kanals befindet sich eine Öffnung, durch die der Schall heraustreten kann. Dieser Schall kann unter geeigneten Voraussetzungen dazu verwendet werden, den Amplituden-Frequenzgang einer elektrodynamischen Lautsprecherbox am tieffrequenten Ende zu erweitern und zu verbessern. Ausgehend von idealisierten theoretischen Ansätzen zum Abstrahlverhalten von Lautsprechern und mit der Theorie der quasihomogenen Absorber wird in diesem Buch darauf eingegangen, wie der Tieftonbereich einer Transmissionline-Lautsprecherbox dimensioniert und akustisch gedämpft werden kann. Dazu werden wegen des einfachen mathematischen Formalismus Funktionen komplexer Zahlen ({{w|Eulersche Formel}}) herangezogen. Dieses Wikibook basiert auf der privaten Vorveröffentlichung ''Anleitung zur akustischen Berechnung einer elektrodynamischen "transmission line"-Lautsprecherbox'' aus dem Jahr 2000. Das hier vorgestellte Berechnungsverfahren wurde vom Autor im Jahr 1986 entwickelt und für den Bau von Transmissionline-Lautsprecherboxen angewandt. <div style="clear:both"></div> == Abstrahlverhalten von Lautsprechern == [[Bild:Amplitudenfrequenzgang.Lautsprecher.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang eines elektrodynamischen Lautsprechers.]] Der Amplitudenfrequenzgang <math>A_0</math> eines elektrodynamischen Lautsprechers mit der Gesamtgüte <math>Q</math> und der Eigenfrequenz <math>f_0</math> berechnet sich über der Schallfrequenz <math>f</math> theoretisch zu :<math>A_0 (f) = \frac {\frac {f} {f_0}} {\sqrt {\frac {1} {Q^2} + \left(\frac {f} {f_0} - \frac {f_0} {f}\right)^2}}</math> Im Resonanzfall <math>f = f_0</math> vereinfacht sich diese Gleichung zu :<math>A_0 (f_0) = Q</math> Mit zunehmender Frequenz fällt der Frequenzgang jedoch entgegen der Aussage obiger Gleichung wieder ab, da die Lautsprechermembran wegen ihrer mechanischen Trägheit nicht mehr mitschwingen kann. Weitere Abweichungen ergeben sich in der Praxis, weil auch andere Eigenfrequenzen als <math>f_0</math> noch kleine Beiträge zum Frequenzgang leisten. Diese Eigenfrequenzen werden im Allgemeinen durch ungewollte Schwingungsmoden der Lautsprechermembran verursacht. <div style="clear:both"></div> === Freier Lautsprecher === [[Bild:Freier.Lautsprecher.gif|upright=2|miniatur|rechts|Freier Lautsprecher.]] Wird ein Lautsprecher bei der Frequenz <math>f</math> ohne Schallwand oder Gehäuse betrieben, so interferieren an der Stelle <math>x = 0</math> (also an der Lautsprecheroberfläche) über der Zeit <math>t</math> die nach vorne abgegebene Schallwelle :<math>A_v = A(f,t) = A_0(f) \cdot e^{-i \omega t}</math> und die nach hinten abgestrahlte Schallwelle :<math>A_h = -A(f,t) = -A_0(f) \cdot e^{-i \omega t}</math> mit der {{w|Eulersche Zahl|Eulerschen Zahl}} <math>e</math>, der {{w|Imaginäre Zahl|imaginären Einheit}} <math>i = \sqrt {-1}</math> und der {{w|Kreisfrequenz}} <math>\omega = 2 \pi f</math> zu allen Zeitpunkten destruktiv: :<math>A_{ges} = A_v + A_h = 0</math> Dies bedeutet, dass die gesamte Amplitudenfrequenzgang <math>A_{ges}</math> null wird, da sich die beiden Schallwellen gegenseitig auslöschen. Dies wird auch als {{w|Akustischer Kurzschluss|akustischer Kurzschluss}} bezeichnet. <div style="clear:both"></div> === Lautsprecher mit Schallwand === [[Bild:Lautsprecher.mit.Schallwand.gif|upright=2|miniatur|rechts|Lautsprecher mit unendlich ausgedehnter Schallwand.]] Die Probleme beim freien Lautsprecher können umgangen werden, wenn der Lautsprecher in eine im Verhältnis zur größten abzustrahlenden Schallwellenlänge möglichst weit ausgedehnte Schallwand eingebaut wird. Allerdings muss eine solche Schallwand meist sehr große Abmessungen haben. Bei einer minimalen Frequenz von beispielsweise 30&nbsp;Hertz müsste die Schallwand seitlich mehrere Meter Ausdehnung haben. :<math>A_{ges} = A_v = A(f,t) = A_0(f)\cdot e^{-i \omega t}</math> wobei <math>A_v</math> die nach vorne abgestrahlte Schallwelle repräsentiert, die in diesem Fall mit der gesamten abgestrahlten <math>A_{ges}</math> identisch ist. <div style="clear:both"></div> === Schallabyrinth === [[Bild: Schallabyrinth.gif|upright=2|miniatur|rechts|Schalllabyrinth.]] Eine Alternative wäre der Einbau des Lautsprechers in ein sogenanntes '''Schallabyrinth'''. Die Schallenergie der nach hinten abgegebenen Schallwelle wird hier durch Mehrfachreflexion und Mehrfachstreuung in Verbindung mit Absorption vernichtet. Auch ein solches Schallabyrinth muss jedoch sehr groß dimensioniert werden, damit der Schall weitgehend absorbiert werden kann. Wie auch bei der Schallwand besteht der Nachteil, dass die vom Lautsprecher nach hinten abgestrahlte Schallenergie nicht genutzt wird. <div style="clear:both"></div> == Geschlossene Lautsprecherbox == [[Bild:Geschlossene.Lautsprecherbox.gif|upright=2|miniatur|rechts|Geschlossene Lautsprecherbox.]] Das ideale Verhalten eines Schalllabyrinths wird bei '''Lautsprecherboxen in geschlossener Bauweise''' nicht erreicht. Neben der nach vorne abgestrahlten Schallwelle <math>A_v</math> können auch die nach hinten abgestrahlten Schallwellen <math>A_h</math>, die an den Gehäuseinnenwänden reflektiert werden, durch die Lautsprecheröffnung hinaustreten. Als Folge der Reflexionen treten ferner stehende Wellen auf, die sich negativ auf den Amplituden- und Phasenfrequenzgang der Lautsprecherbox auswirken. Dies kann zwar eingeschränkt werden, indem das Innere der Lautsprecherbox mit einem schallschluckenden Medium gefüllt wird und die Gehäusewände nicht parallel ausgerichtet sind, jedoch lässt sich dieser Effekt nie vollständig vermeiden. Ferner kann besonders bei niedrigen Frequenzen ein so hoher Schalldruck auftreten, dass eine Rückkopplung auf die Lautsprechermembran stattfindet, die sich ebenso wie die reflektierten Schallwellen als nichtlinearer Effekt negativ bemerkbar macht. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich aus der Überlagerung aller Schallwellen: :<math>A_{ges} = A_v + B(A_h) + C(A_h)</math>, wobei die Funktion <math>B(A_h)</math> die im Lautsprechergehäuse gedämpften und reflektieren Schallwellen repräsentiert und <math>C(A_h)</math> die nichtlinearen Anteile durch Rückkopplung des Schalls. <div style="clear:both"></div> === Teilventilierte Lautsprecherbox === [[Bild:Teilventilierte.Lautsprecherbox.gif|upright=2|miniatur|rechts|Teilventilierte Lautsprecherbox.]] Der hohe Schalldruck wird bei den sogenannten '''teilventilierten Lautsprecherboxen''' durch eine kleine Öffnung vermieden, die zum Druckausgleich dient. Wenn solche Lautsprecherboxen gut bedämpft sind und keine parallelen Gehäusewände haben, damit sich keine stehenden Wellen aufbauen können, stellen sie einen recht guten Kompromiss dar, die nach hinten abgestrahlte Schallenergie findet aber auch hier keine Verwendung bei der Klangwiedergabe. <div style="clear:both"></div> === Bass-Reflex-Lautsprecherbox === [[Bild:Bass.Reflex.Lautsprecherbox.gif|upright=2|miniatur|rechts|Bass-Reflex-Lautsprecherbox.]] Bei '''Bass-Reflex-Lautsprecherboxen''' wird die Eigenfrequenz des Lautsprechergehäuses bewusst eingesetzt, um die Schallabstrahlung der Lautsprecherbox meist im Tieftonbereich zu verstärken. Die Lautsprecherbox hat eine zusätzliche, meist nach vorn gerichtete Rohröffnung und wirkt somit als Helmholtz-Resonator mit der Amplitudenfunktion <math>B_v(f,t)</math>. Es ist auf diese Weise jedoch problematisch, einen gleichmäßigen Frequenzgang zu erreichen, da in der Regel eine zu starke Verstärkung in einem zu schmalen Frequenzband besteht und dadurch die beiden nach vorne abgestrahlten Schallwellen vom Lautsprecher <math>A_v</math> und von der zusätzlichen Öffnung <math>B_v</math> sehr schlecht aufeinander abgestimmt werden können. Auch bei Bass-Reflex-Lautsprecherboxen kann es wie bei geschlossenen Lautsprecherboxen natürlich zusätzlich noch zu stehenden Wellen kommen. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich also im Wesentlichen aus der Überlagerung zweier Schallwellen: :<math>A_{ges} = A_v + B_v</math>, wobei die Funktion <math>A_v</math> die nach vorne abgestrahlte Schallwelle und <math>B_v</math> die aus der Bass-Reflex-Öffnung abgestrahlten Schallwellen repräsentiert. <div style="clear:both"></div> == Transmissionline-Lautsprecherbox == === Transmissionline ohne Dämpfung === [[Bild:Transmissionline.Lautsprecherbox.ohne.Daempfung.gif|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox ohne Dämpfung.]] Die vom Lautsprecher nach hinten abgegebene Schallwelle <math>A_h</math> kann durch einen Schallkanal mit der Länge <math>X_0</math> geleitet werden, an dessen Ende sich eine Öffnung befindet. Dieser idealerweise resonanzfreie Schallkanal wurde von dem an der Universität von Bradford lehrenden Briten Arthur R.&nbsp;Bailey '''Transmissionline''' genannt. Die aus dieser Öffnung heraustretende Schallwelle <math>A_{h,TL}</math> kann mit der vom Lautsprecher nach vorn abgestrahlten Schallwelle <math>A_v</math> interferieren. Die Amplitude der interferierten Schallwelle ergibt sich im Falle des ungedämpften Schallkanals aus der Summe der beiden Anteile: :<math>A_v = A_0(f)</math> wie beim freien Lautsprecher und :<math>A_{h,TL} = -A_0(f)\cdot e^{-i \omega \cdot {X_0 \over c_0}}</math> Hierbei ist <math>c_0</math> die Schallgeschwindigkeit im gasförmigen Schallmedium (in Luft bei 20°&nbsp;C und 60% relativer Luftfeuchtigkeit ist <math>c_0 = 344 {\frac {m} {s}}</math>). An der Lautsprecherfront ergibt sich für <math>A_{ges,TL}</math> ohne Dämpfung also: :<math>A_{ges,TL} = A_v + A_{h,TL} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {\frac {X_0} {c_0}}}\right)</math> <div style="clear:both"></div> === Transmissionline mit Dämpfung === ==== Qualitative Beschreibung ==== In den 1960er Jahren kam Bailey auf die Idee, die von einem Tieftöner nach hinten abgestrahlte Schallenergie in einem hinreichend langen, bedämpften und am Ende offenen Schallkanal quasi "totlaufen" zu lassen, was er in zwei Artikeln im Fachmagazin Wireless World beschrieben hat. Die Dämpfung ist dabei ein integraler Bestandteil der Konstruktion von Bailey. Die Dämpfung, die Schallgeschwindigkeit und somit auch die Wellenlänge der Schallwellen sind bei einem solchen Tiefpass frequenzabhängig (Dispersion). Die Schallwellen werden umso weniger stark gedämpft, je geringer die Frequenz beziehungsweise je größer die Wellenlänge der Schallwellen ist. Die höherfrequenten Anteile werden durch die akustische Dämpfung in Wärmeenergie umgewandelt, und die sehr tieffrequenten am Ende aus der Transmissionline noch austretenden Schallwellen können bei geeigneter Interferenz mit den direkten, nach vorne abgegebenen Schallwellen des eingesetzten Tieftöners den Amplitudenfrequenzgang bei tiefen Frequenzen verbessern. Die ersten nach diesem Prinzip gebauten kommerziellen Lautsprecherboxen wurden zunächst von der in Cambridgeshire ansässigen und mit Bailey kooperierenden Firma Radford und später unter anderem von IMF Electronics und in Deutschland von {{w|Lautsprecher Teufel}} auf den Markt gebracht. Die vom Lautsprecher nach hinten abgegebene Schallwelle wird in dem zum Beispiel mit langfaseriger Schafwolle gedämmten Schallkanal zunehmend abgeschwächt, sehr tiefe Frequenzen können den Schallkanal jedoch nur schwach gedämpft verlassen und zur Schallwiedergabe verwendet werden. Die Länge des Kanals sollte nach Bailey mindestens etwa ein Viertel der Wellenlänge der unteren Grenzfrequenz betragen, kann aber auch länger sein. Er kann in verschiedener Weise geknickt oder gefaltet werden, um in einem konventionellen Gehäuse Platz zu finden, jedoch sollten Reflexionen (zum Beispiel mit gekrümmten oder abgeschrägten Flächen) vermieden werden. Da bei diesem "nicht-resonanten Lautsprechergehäuse" (Bailey) ein großer Teil der nach hinten abgestrahlte Schallenergie vernichtet wird, ist der Wirkungsgrad von Transmissionline-Lautsprechern relativ schlecht, was jedoch dank leistungsfähiger (Transistor-)Verstärker normalerweise keine Rolle spielt. Bei korrekter Konstruktion zeichnen sich Transmissionline-Lautsprecher durch sehr gute, klangneutrale (Tief-)Basswiedergabe und sehr gutes Impulsverhalten aus. Wegen Größe, Gewicht und der relativ hohen Fertigungskosten konnten sie sich jedoch auf lange Sicht nicht auf dem Markt durchsetzen. <gallery caption="Tieffrequente Sinustöne mit 30 Sekunden Dauer" perrow="5"> Sine Wave 10Hz 30s.ogg|10 Hertz Sine Wave 20Hz 30s.ogg|20 Hertz Sine Wave 30Hz 30s.ogg|30 Hertz Sine Wave 40Hz 30s.ogg|40 Hertz Sine Wave 50Hz 30s.ogg|50 Hertz </gallery> ==== Quantitative Berechnung ==== [[Bild:Transmissionline.Lautsprecherbox.mit.Daempfung.png|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox mit Dämpfung.]] Im folgenden werden die von Bailey im Wesentlichen nur qualitativ beschriebenen Sachverhalte anhand entsprechender akustischer Formeln quantitativ beschrieben. Wird die Transmissionline mit einem porösen, faserigen Absorber bedämpft, so kann die aus der Transmissionline mit der Länge <math>X_0</math> austretende Schallwellenamplitude <math>A_{h,TL,D}</math> nach Fridolin Peter Mechel nach der '''Theorie der quasihomogenen Absorber''' mit der komplexwertigen Ausbreitungskonstante <math>\Gamma</math> näherungsweise angegeben werden. Die dimensionslose Hilfsgröße <math>\Omega</math> steht hierbei für das Verhältnis des Trägheitswiderstands der Luft <math>t = \rho_0 \cdot \omega</math> zum spezifischen Strömungswiderstand des porösen Absorbermaterials <math>r</math>, was ähnlich wie eine Reynolds-Zahl interpretiert werden kann: :<math>\Omega = \frac {t} {r} = \frac {\rho_0 \cdot \omega} {r}</math> :<math>\Gamma = \frac {i \omega} {c_0} \cdot \sqrt {1 - \frac {i} {\Omega}} = \frac {i \omega} {c_0} \cdot \sqrt {1 - i \cdot \frac {r} {\rho_0 \cdot \omega}}</math> :<math>A_{h,TL,D} = -A_0 (f) \cdot e^ {- \Gamma \cdot X_0} = -A_0 (f) \cdot e^ {-i \omega \cdot \frac {X_0} {c_0} \cdot \sqrt {1 - i \cdot \frac {r} {\rho_0 \cdot \omega}}}</math> Dabei ist <math>\rho_0</math> die Dichte des gasförmigen Schallmediums (bei trockener Luft bei 20°&nbsp;C ist <math>\rho_0 = 1.2 {\text{kg} \over \text{m}^3}</math>). Je größer der spezifische Strömungswiderstand, desto stärker ist die Schalldämpfung. Er hängt im Wesentlichen vom Material, von der Faserfeinheit und von der Massendichte <math>\rho</math>, also von der Packungsdichte, des Absorbers und vom Füllgrad in der Transmissionline ab. Weiterhin hängt er von der gleichmäßigen Verteilung des Absorbers aber auch von Faserrichtung und der Schallfrequenz ab, so dass in der folgenden Tabelle nur ungefähre Richtwerte für einige poröse, faserige Absorber angeben sind: {| class="wikitable zebra" | style="text-align:center" !Material !Massendichte !Spezifischer<br/>Strömungswiderstand !Gewichtseffizienz |- | |<math>\rho</math> in <math>\frac {\text{kg}} {\text{m}^3}</math> |<math>r</math> in <math>\frac {\text{Ns}} {\text{m}^4}</math> |<math>\eta_m = \frac {r} {\rho}</math> in <math>\frac {\text{Ns}} {\text{kg m}} = \text{Hz}</math> |- |Schafwolle |5 |4000 |800 |- |Schafwolle |10 |8000 |800 |- |Baumwolle |5 |1000 |200 |- |Baumwolle |10 |4000 |400 |- |Glaswolle |50 |200 |4 |- |Glasfaserplatten |20 |1000 |50 |- |Mineralwolle |10 |600 |60 |- |Mineralwolle |50 |10000 |200 |- |Aluminiumwolle |35 |500 |14,3 |- |Aluminiumwolle |70 |4000 |57,1 |} Die Gewichtseffizienz des Absorbermaterials <math>\eta_m</math> ist in der Tabelle als das Verhältnis des spezifischen Strömungswiderstands <math>r</math> zur Massendichte <math>\rho</math> angegeben: :<math>\eta_m = \frac {r} {\rho}</math> [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Laengen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Längen.]] [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Daempfungen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Dämpfungen.]] Für die gesamte Transmissionline-Lautsprecherbox ergibt sich an der Front der Lautsprecherbox mit Dämpfung durch die Überlagerung des direkten Schalls mit dem Schall aus der gedämpften Transmissionline die folgende frequenzabhängige Amplitude: :<math>A_{ges,TL,D} = A_v + A_{h,TL,D} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {X_0 \over c_0} \cdot \sqrt{1 - i \cdot {r \over {\rho_0 \cdot \omega}}}}\right)</math> Der Amplitudenverlauf ergibt sich aus dem Betrag der komplexen Funktion <math>A_{ges,TL,D}</math>. Dieser muss entsprechend den Parametern Gesamtgüte des Tieftöners <math>Q</math>, Länge der Transmissionline <math>X_0</math> und spezifischer Strömungswiderstand <math>r</math> so angepasst werden, dass bei den tiefen Frequenzen ein möglichst gleichmäßiger Amplitudenverlauf entsteht. Die beiden nebenstehenden Abbildungen sollen den Einfluss der beiden Parameter <math>X_0</math> und <math>r</math> bei vorgegebenem <math>Q</math> verdeutlichen. Die als geeignet bestimmten Parameter können beim Bau einer Transmissionline-Lautsprecherbox umgesetzt werden. Insbesondere die richtige Bedämpfung muss hierbei jedoch experimentell überprüft und gegebenenfalls korrigiert werden. Ferner muss bei der Ansteuerung von Tief- und Mitteltöner die aus obiger Gleichung resultierende Amplitude :<math>|A_{ges,TL,D}|</math> und Phasendifferenz :<math>arg(A_{ges,TL,D})</math> bei der Übergangsfrequenz zwischen dem System aus Transmissionline und Tieftöner auf der einen Seite und Mitteltöner auf der anderen Seite mittels der Frequenzweiche ausgeglichen werden. <div style="clear:both"></div> === Erfahrungswerte für den Entwurf einer Transmissionline-Lautsprecherbox === [[Datei:Transmissionline-Lautsprecher.P1117437.jpg|mini|hochkant=1.5|1,2&nbsp;Meter hohe Transmissionline-Lautsprecherbox aus dem Jahr 1986 mit trapezförmiger Grundfläche (Breite vorne = 30&nbsp;Zentimeter, Breite hinten = 50&nbsp;Zentimeter, Tiefe = 27&nbsp;Zentimeter). Oben befindet sich der Tieftöner, darunter der Mitteltöner und unten der Hochtöner. Die beiden Austrittsöffnungen der unten im Gehäuse symmetrisch nach rechts und links aufgeteilten akustischen Transmissionlines befinden sich oben an den abgeschrägten Seiten hinter dem Tieftöner. Rechnerisch wird im Frequenzgang beim Unterschreiten von 30&nbsp;Hertz ein Pegelabfall von drei Dezibel erreicht. In einem größeren Abhöraum betrug die Pegelschwankung dieser Kombination aus Tieftöner und Transmissionline im Frequenzgang zwischen 20&nbsp;und 50&nbsp;Hertz ungefähr ein Dezibel.]] Der Querschnitt der Transmissionline direkt hinter dem Lautsprecher sollte mindestens so groß sein wie die Lautsprechermembranfläche oder sogar etwas darüber liegen. Entlang der Transmissionline kann sich deren Querschnitt um maximal 25&nbsp;Prozent verringern. Für den Transmissionline-Betrieb geeignete Tieftöner sollten eine relativ hohe Gesamtgüte <math>\left(Q \lessapprox 1\right)</math> haben. Mit einer passiven Frequenzweiche, über aktive Frequenzfilter beziehungsweise mit Hilfe von digitalen Signalprozessoren werden die elektrischen Signale auf die Tief- und Mittel- sowie gegebenenfalls auch die Hochtöner verteilt. Um unerwünschte Effekte durch akustische Interferenzen zu minimieren, sollten die seitlichen Abstände zwischen Mittel- und Tieftöner sowie zwischen Hoch- und Mitteltöner so gering wie möglich gehalten werden. Außerdem sollten alle Lautsprecher bündig mit dem Gehäuse abschließen und in einer Ebene liegen. Die Austrittsöffnung einer Transmissionline kann konstruktiv wie ein Subtieftöner betrachtet werden. Der Wert des spezifischen Strömungswiderstands <math>r</math> (und somit der Dichte <math>\rho_0</math>) sollte bei der fertiggestellten Lautsprecherbox experimentell überprüft (zum Beispiel mit Frequenzgenerator oder mit Rauschgenerator und Frequenzanalysator) und gegebenenfalls angepasst werden, da er für einen in die Transmissionline eingebauten Absorber meist nicht genau genug berechnet werden kann. Der Mess- oder Abhörraum hat durch seine Eigenfrequenzen im tiefen Frequenzbereich einen deutlichen Einfluss auf die Tieftonwiedergabe. Insbesondere bei parallelen Wänden (respektive Böden und Decken) können stehende Wellen Raumresonanzen hervorrufen, wenn die Schallwellenlänge <math>\lambda</math> dem Doppelten einer Raumdimension mit der Länge <math>s</math> entspricht: :<math>f_{Resonanz} = \frac {c_0} {\lambda} = \frac {c_0} {2 \cdot s}</math> In einem quaderförmigen Raum mit der Länge <math>l</math>, der Breite <math>b</math> und der Höhe <math>h</math> ergibt sich nach dem Satz der Pythagoras die kleinste Resonanzfrequenz aus der Raumdiagonale <math>d</math>: :<math>d = \sqrt {l^2 + b^2 + h^2}</math> :<math>f_{Resonanz, min} = \frac {c_0} {2 \cdot d} = \frac {c_0} {2 \cdot \sqrt {l^2 + b^2 + h^2}}</math> Ein quaderförmiger Raum mit einer quadratischen Grundfläche von 100&nbsp;Quadratmetern hat demnach eine niedrigste Eigenfrequenz von ungefähr 12&nbsp;Hertz. Durch schwere Vorhänge und Teppiche oder andere Schallabsorber kann die Schallreflexion in einem Raum deutlich reduziert werden, wobei die Schallabsorption für tiefer werdende Frequenzen in der Regel zunehmend geringer wird. Im freien Feld beziehungsweise in schallreflexionsarmen Messräumen entfallen solche Raumreflexionen oder sind so stark reduziert, so dass sich für einen gegebenen Lautsprecher ein anderes Klangbild als in einem gewöhnlichen Raum ergibt. {| class="wikitable" |+ Parameter der abgebildeten Transmissionline-Lautsprecherbox ! Parameter !! Formelsymbol !! Wert |- | Eigenresonanzfrequenz des Tieftöners || <math>f_0</math> || <math>\text {31 Hz}</math> |- | Güte des Tieftöners || <math>Q</math> || <math>\text {0,65}</math> |- | Länge der Transmissionline || <math>X_0</math> || <math>\text {2,75 m}</math> |- | Mittlerer spezifischer Widerstand der Dämmung mit Mineralwolle || <math>r</math> || <math>\text {1500 } \frac {\text {Ns}} {\text {m}^4}</math> |- | Mittlere Massendichte der Dämmung mit Mineralwolle || <math>\rho</math> || <math>\text {17,4 } \frac {\text {kg}} {\text {m}^3}</math> |- | Gewichtseffizienz der Dämmung mit Mineralwolle || <math>\eta_m</math> || <math>\text {86 } \text {Hz}</math> |} ==Widmung== Diese Zusammenstellung ist meinem Studienfreund '''Dr.&nbsp;Lutz König''' gewidmet. Der Hauptautor dankt ihm für seine freundschaftlichen, umfangreichen und stets förderlichen Beiträge zur Realisierung unserer 1986 in Eigenentwicklung hergestellten vier Lautsprecherboxen. == Literatur == * Arthur R.&nbsp;Bailey: ''A Non-resonant Loudspeaker Enclosure Design'' - ''Using acoustic transmission line with low-pass filter characteristics'', Wireless World, October 1965, p.&nbsp;483-486 * Arthur R.&nbsp;Bailey: ''The Transmission-line Loudspeaker Enclosure'' - ''A re-examination of the general principle and a suggested new method of construction'', Wireless World, May 1972, p.&nbsp;215-217 * {{w|Ludwig Bergmann (Physiker)|Ludwig Bergmann}}, {{w|Clemens Schaefer (Physiker)|Clemens Schäfer}}: ''{{w|Bergmann-Schaefer_Lehrbuch_der_Experimentalphysik|Lehrbuch der Experimentalphysik}}'', Band 1: ''Mechanik, Akustik, Wärme'', 9. Auflage, de Gruyter, 1974, ISBN 978-3-1100-4861-2 * Fridolin P.&nbsp;Mechel: ''Schallabsorption'', Kapitel&nbsp;18, in: {{w|Manfred Heckl}}, {{w|Helmut A. Müller (Akustiker)|Helmut A.&nbsp;Müller}}: ''Taschenbuch der technischen Akustik'', Springer-Verlag, Berlin, Heidelberg, New York, 1975, ISBN 3-6429-7357-4 * Hans Herbert Klinger: ''Lautsprecher und Lautsprechergehäuse für HiFi'', Franzis-Verlag GmbH, München, 1981, ISBN 3-7723-1051-6 * Heinz Sahm: ''HiFi-Lautsprecher'', ''Grundlagen der elektrodynamischen Lautsprecher in unendlicher Schallwand und im Gehäuse'', Franzis-Verlag GmbH, München, 1982, ISBN 3-7723-6522-1 * Berndt Stark: ''Lautsprecher-Handbuch'' - ''Theorie und Praxis des Boxenbauens'', Richard Pflaum Verlag, München, 1985, ISBN 3-7905-0433-5 == Siehe auch == * [[Grundlegendes zur Akustik]] == Weblinks == {{Commonscat|Transmission line loudspeakers}} == Zusammenfassung des Projekts == {{Vorlage:StatusBuch|10}} * '''Zielgruppe und Lernziele:''' Dieses Buch richtet sich an akustisch interessierte Leser mit mathematischer Ausbildung. Es beschreibt einen vollständigen Weg zur Berechnung einer bedämpften akustischen Transmissionline mit komplexwertigen Funktionen. Aufbauend auf grundlegenden Betrachtungen zur Schallerzeugung und -abstrahlung von Lautsprechern wird durch schrittweise Erweiterungen und Ergänzungen die Funktionsweise einer solchen Transmissionline erarbeitet. * '''Ansprechperson:''' Ansprechpartner ist [[Benutzer:Bautsch]] [[Kategorie:Geometrische Kuriositäten‎]] d2deferl97sxytov1smbq7s2xv7tgrh 1088127 1088125 2026-06-14T07:39:38Z Bautsch 35687 /* Quantitative Berechnung */ Mechel (1930–2014) 1088127 wikitext text/x-wiki {{Regal | ort=Physik}} {{Infoleiste|Physik|Studium|S|Technik}} [[Bild:Transmission-line.png|rechts|mini|Beispiel für eine Transmissionline als akustischen Kanal (blaue Pfeile) für den rückwärtigen Schall eines Lautsprechers (orangefarben) in einer Lautsprecherbox (dunkelbraun).]] == Einführung == '''Transmissionline''' ("Übertragungsleitung") nennt man in der Lautsprecherakustik einen in der Regel mehrfach gefalteten Kanal, durch den der von einem Lautsprecher nach hinten abgegebene Schall hindurchtritt. Am Ende dieses Kanals befindet sich eine Öffnung, durch die der Schall heraustreten kann. Dieser Schall kann unter geeigneten Voraussetzungen dazu verwendet werden, den Amplituden-Frequenzgang einer elektrodynamischen Lautsprecherbox am tieffrequenten Ende zu erweitern und zu verbessern. Ausgehend von idealisierten theoretischen Ansätzen zum Abstrahlverhalten von Lautsprechern und mit der Theorie der quasihomogenen Absorber wird in diesem Buch darauf eingegangen, wie der Tieftonbereich einer Transmissionline-Lautsprecherbox dimensioniert und akustisch gedämpft werden kann. Dazu werden wegen des einfachen mathematischen Formalismus Funktionen komplexer Zahlen ({{w|Eulersche Formel}}) herangezogen. Dieses Wikibook basiert auf der privaten Vorveröffentlichung ''Anleitung zur akustischen Berechnung einer elektrodynamischen "transmission line"-Lautsprecherbox'' aus dem Jahr 2000. Das hier vorgestellte Berechnungsverfahren wurde vom Autor im Jahr 1986 entwickelt und für den Bau von Transmissionline-Lautsprecherboxen angewandt. <div style="clear:both"></div> == Abstrahlverhalten von Lautsprechern == [[Bild:Amplitudenfrequenzgang.Lautsprecher.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang eines elektrodynamischen Lautsprechers.]] Der Amplitudenfrequenzgang <math>A_0</math> eines elektrodynamischen Lautsprechers mit der Gesamtgüte <math>Q</math> und der Eigenfrequenz <math>f_0</math> berechnet sich über der Schallfrequenz <math>f</math> theoretisch zu :<math>A_0 (f) = \frac {\frac {f} {f_0}} {\sqrt {\frac {1} {Q^2} + \left(\frac {f} {f_0} - \frac {f_0} {f}\right)^2}}</math> Im Resonanzfall <math>f = f_0</math> vereinfacht sich diese Gleichung zu :<math>A_0 (f_0) = Q</math> Mit zunehmender Frequenz fällt der Frequenzgang jedoch entgegen der Aussage obiger Gleichung wieder ab, da die Lautsprechermembran wegen ihrer mechanischen Trägheit nicht mehr mitschwingen kann. Weitere Abweichungen ergeben sich in der Praxis, weil auch andere Eigenfrequenzen als <math>f_0</math> noch kleine Beiträge zum Frequenzgang leisten. Diese Eigenfrequenzen werden im Allgemeinen durch ungewollte Schwingungsmoden der Lautsprechermembran verursacht. <div style="clear:both"></div> === Freier Lautsprecher === [[Bild:Freier.Lautsprecher.gif|upright=2|miniatur|rechts|Freier Lautsprecher.]] Wird ein Lautsprecher bei der Frequenz <math>f</math> ohne Schallwand oder Gehäuse betrieben, so interferieren an der Stelle <math>x = 0</math> (also an der Lautsprecheroberfläche) über der Zeit <math>t</math> die nach vorne abgegebene Schallwelle :<math>A_v = A(f,t) = A_0(f) \cdot e^{-i \omega t}</math> und die nach hinten abgestrahlte Schallwelle :<math>A_h = -A(f,t) = -A_0(f) \cdot e^{-i \omega t}</math> mit der {{w|Eulersche Zahl|Eulerschen Zahl}} <math>e</math>, der {{w|Imaginäre Zahl|imaginären Einheit}} <math>i = \sqrt {-1}</math> und der {{w|Kreisfrequenz}} <math>\omega = 2 \pi f</math> zu allen Zeitpunkten destruktiv: :<math>A_{ges} = A_v + A_h = 0</math> Dies bedeutet, dass die gesamte Amplitudenfrequenzgang <math>A_{ges}</math> null wird, da sich die beiden Schallwellen gegenseitig auslöschen. Dies wird auch als {{w|Akustischer Kurzschluss|akustischer Kurzschluss}} bezeichnet. <div style="clear:both"></div> === Lautsprecher mit Schallwand === [[Bild:Lautsprecher.mit.Schallwand.gif|upright=2|miniatur|rechts|Lautsprecher mit unendlich ausgedehnter Schallwand.]] Die Probleme beim freien Lautsprecher können umgangen werden, wenn der Lautsprecher in eine im Verhältnis zur größten abzustrahlenden Schallwellenlänge möglichst weit ausgedehnte Schallwand eingebaut wird. Allerdings muss eine solche Schallwand meist sehr große Abmessungen haben. Bei einer minimalen Frequenz von beispielsweise 30&nbsp;Hertz müsste die Schallwand seitlich mehrere Meter Ausdehnung haben. :<math>A_{ges} = A_v = A(f,t) = A_0(f)\cdot e^{-i \omega t}</math> wobei <math>A_v</math> die nach vorne abgestrahlte Schallwelle repräsentiert, die in diesem Fall mit der gesamten abgestrahlten <math>A_{ges}</math> identisch ist. <div style="clear:both"></div> === Schallabyrinth === [[Bild: Schallabyrinth.gif|upright=2|miniatur|rechts|Schalllabyrinth.]] Eine Alternative wäre der Einbau des Lautsprechers in ein sogenanntes '''Schallabyrinth'''. Die Schallenergie der nach hinten abgegebenen Schallwelle wird hier durch Mehrfachreflexion und Mehrfachstreuung in Verbindung mit Absorption vernichtet. Auch ein solches Schallabyrinth muss jedoch sehr groß dimensioniert werden, damit der Schall weitgehend absorbiert werden kann. Wie auch bei der Schallwand besteht der Nachteil, dass die vom Lautsprecher nach hinten abgestrahlte Schallenergie nicht genutzt wird. <div style="clear:both"></div> == Geschlossene Lautsprecherbox == [[Bild:Geschlossene.Lautsprecherbox.gif|upright=2|miniatur|rechts|Geschlossene Lautsprecherbox.]] Das ideale Verhalten eines Schalllabyrinths wird bei '''Lautsprecherboxen in geschlossener Bauweise''' nicht erreicht. Neben der nach vorne abgestrahlten Schallwelle <math>A_v</math> können auch die nach hinten abgestrahlten Schallwellen <math>A_h</math>, die an den Gehäuseinnenwänden reflektiert werden, durch die Lautsprecheröffnung hinaustreten. Als Folge der Reflexionen treten ferner stehende Wellen auf, die sich negativ auf den Amplituden- und Phasenfrequenzgang der Lautsprecherbox auswirken. Dies kann zwar eingeschränkt werden, indem das Innere der Lautsprecherbox mit einem schallschluckenden Medium gefüllt wird und die Gehäusewände nicht parallel ausgerichtet sind, jedoch lässt sich dieser Effekt nie vollständig vermeiden. Ferner kann besonders bei niedrigen Frequenzen ein so hoher Schalldruck auftreten, dass eine Rückkopplung auf die Lautsprechermembran stattfindet, die sich ebenso wie die reflektierten Schallwellen als nichtlinearer Effekt negativ bemerkbar macht. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich aus der Überlagerung aller Schallwellen: :<math>A_{ges} = A_v + B(A_h) + C(A_h)</math>, wobei die Funktion <math>B(A_h)</math> die im Lautsprechergehäuse gedämpften und reflektieren Schallwellen repräsentiert und <math>C(A_h)</math> die nichtlinearen Anteile durch Rückkopplung des Schalls. <div style="clear:both"></div> === Teilventilierte Lautsprecherbox === [[Bild:Teilventilierte.Lautsprecherbox.gif|upright=2|miniatur|rechts|Teilventilierte Lautsprecherbox.]] Der hohe Schalldruck wird bei den sogenannten '''teilventilierten Lautsprecherboxen''' durch eine kleine Öffnung vermieden, die zum Druckausgleich dient. Wenn solche Lautsprecherboxen gut bedämpft sind und keine parallelen Gehäusewände haben, damit sich keine stehenden Wellen aufbauen können, stellen sie einen recht guten Kompromiss dar, die nach hinten abgestrahlte Schallenergie findet aber auch hier keine Verwendung bei der Klangwiedergabe. <div style="clear:both"></div> === Bass-Reflex-Lautsprecherbox === [[Bild:Bass.Reflex.Lautsprecherbox.gif|upright=2|miniatur|rechts|Bass-Reflex-Lautsprecherbox.]] Bei '''Bass-Reflex-Lautsprecherboxen''' wird die Eigenfrequenz des Lautsprechergehäuses bewusst eingesetzt, um die Schallabstrahlung der Lautsprecherbox meist im Tieftonbereich zu verstärken. Die Lautsprecherbox hat eine zusätzliche, meist nach vorn gerichtete Rohröffnung und wirkt somit als Helmholtz-Resonator mit der Amplitudenfunktion <math>B_v(f,t)</math>. Es ist auf diese Weise jedoch problematisch, einen gleichmäßigen Frequenzgang zu erreichen, da in der Regel eine zu starke Verstärkung in einem zu schmalen Frequenzband besteht und dadurch die beiden nach vorne abgestrahlten Schallwellen vom Lautsprecher <math>A_v</math> und von der zusätzlichen Öffnung <math>B_v</math> sehr schlecht aufeinander abgestimmt werden können. Auch bei Bass-Reflex-Lautsprecherboxen kann es wie bei geschlossenen Lautsprecherboxen natürlich zusätzlich noch zu stehenden Wellen kommen. Die gesamte abgestrahlte Schallwelle <math>A_{ges}</math> ergibt sich also im Wesentlichen aus der Überlagerung zweier Schallwellen: :<math>A_{ges} = A_v + B_v</math>, wobei die Funktion <math>A_v</math> die nach vorne abgestrahlte Schallwelle und <math>B_v</math> die aus der Bass-Reflex-Öffnung abgestrahlten Schallwellen repräsentiert. <div style="clear:both"></div> == Transmissionline-Lautsprecherbox == === Transmissionline ohne Dämpfung === [[Bild:Transmissionline.Lautsprecherbox.ohne.Daempfung.gif|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox ohne Dämpfung.]] Die vom Lautsprecher nach hinten abgegebene Schallwelle <math>A_h</math> kann durch einen Schallkanal mit der Länge <math>X_0</math> geleitet werden, an dessen Ende sich eine Öffnung befindet. Dieser idealerweise resonanzfreie Schallkanal wurde von dem an der Universität von Bradford lehrenden Briten Arthur R.&nbsp;Bailey '''Transmissionline''' genannt. Die aus dieser Öffnung heraustretende Schallwelle <math>A_{h,TL}</math> kann mit der vom Lautsprecher nach vorn abgestrahlten Schallwelle <math>A_v</math> interferieren. Die Amplitude der interferierten Schallwelle ergibt sich im Falle des ungedämpften Schallkanals aus der Summe der beiden Anteile: :<math>A_v = A_0(f)</math> wie beim freien Lautsprecher und :<math>A_{h,TL} = -A_0(f)\cdot e^{-i \omega \cdot {X_0 \over c_0}}</math> Hierbei ist <math>c_0</math> die Schallgeschwindigkeit im gasförmigen Schallmedium (in Luft bei 20°&nbsp;C und 60% relativer Luftfeuchtigkeit ist <math>c_0 = 344 {\frac {m} {s}}</math>). An der Lautsprecherfront ergibt sich für <math>A_{ges,TL}</math> ohne Dämpfung also: :<math>A_{ges,TL} = A_v + A_{h,TL} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {\frac {X_0} {c_0}}}\right)</math> <div style="clear:both"></div> === Transmissionline mit Dämpfung === ==== Qualitative Beschreibung ==== In den 1960er Jahren kam Bailey auf die Idee, die von einem Tieftöner nach hinten abgestrahlte Schallenergie in einem hinreichend langen, bedämpften und am Ende offenen Schallkanal quasi "totlaufen" zu lassen, was er in zwei Artikeln im Fachmagazin Wireless World beschrieben hat. Die Dämpfung ist dabei ein integraler Bestandteil der Konstruktion von Bailey. Die Dämpfung, die Schallgeschwindigkeit und somit auch die Wellenlänge der Schallwellen sind bei einem solchen Tiefpass frequenzabhängig (Dispersion). Die Schallwellen werden umso weniger stark gedämpft, je geringer die Frequenz beziehungsweise je größer die Wellenlänge der Schallwellen ist. Die höherfrequenten Anteile werden durch die akustische Dämpfung in Wärmeenergie umgewandelt, und die sehr tieffrequenten am Ende aus der Transmissionline noch austretenden Schallwellen können bei geeigneter Interferenz mit den direkten, nach vorne abgegebenen Schallwellen des eingesetzten Tieftöners den Amplitudenfrequenzgang bei tiefen Frequenzen verbessern. Die ersten nach diesem Prinzip gebauten kommerziellen Lautsprecherboxen wurden zunächst von der in Cambridgeshire ansässigen und mit Bailey kooperierenden Firma Radford und später unter anderem von IMF Electronics und in Deutschland von {{w|Lautsprecher Teufel}} auf den Markt gebracht. Die vom Lautsprecher nach hinten abgegebene Schallwelle wird in dem zum Beispiel mit langfaseriger Schafwolle gedämmten Schallkanal zunehmend abgeschwächt, sehr tiefe Frequenzen können den Schallkanal jedoch nur schwach gedämpft verlassen und zur Schallwiedergabe verwendet werden. Die Länge des Kanals sollte nach Bailey mindestens etwa ein Viertel der Wellenlänge der unteren Grenzfrequenz betragen, kann aber auch länger sein. Er kann in verschiedener Weise geknickt oder gefaltet werden, um in einem konventionellen Gehäuse Platz zu finden, jedoch sollten Reflexionen (zum Beispiel mit gekrümmten oder abgeschrägten Flächen) vermieden werden. Da bei diesem "nicht-resonanten Lautsprechergehäuse" (Bailey) ein großer Teil der nach hinten abgestrahlte Schallenergie vernichtet wird, ist der Wirkungsgrad von Transmissionline-Lautsprechern relativ schlecht, was jedoch dank leistungsfähiger (Transistor-)Verstärker normalerweise keine Rolle spielt. Bei korrekter Konstruktion zeichnen sich Transmissionline-Lautsprecher durch sehr gute, klangneutrale (Tief-)Basswiedergabe und sehr gutes Impulsverhalten aus. Wegen Größe, Gewicht und der relativ hohen Fertigungskosten konnten sie sich jedoch auf lange Sicht nicht auf dem Markt durchsetzen. <gallery caption="Tieffrequente Sinustöne mit 30 Sekunden Dauer" perrow="5"> Sine Wave 10Hz 30s.ogg|10 Hertz Sine Wave 20Hz 30s.ogg|20 Hertz Sine Wave 30Hz 30s.ogg|30 Hertz Sine Wave 40Hz 30s.ogg|40 Hertz Sine Wave 50Hz 30s.ogg|50 Hertz </gallery> ==== Quantitative Berechnung ==== [[Bild:Transmissionline.Lautsprecherbox.mit.Daempfung.png|upright=2|miniatur|rechts|Transmissionline-Lautsprecherbox mit Dämpfung.]] Im folgenden werden die von Bailey im Wesentlichen nur qualitativ beschriebenen Sachverhalte anhand entsprechender akustischer Formeln quantitativ beschrieben. Wird die Transmissionline mit einem porösen, faserigen Absorber bedämpft, so kann die aus der Transmissionline mit der Länge <math>X_0</math> austretende Schallwellenamplitude <math>A_{h,TL,D}</math> nach '''Fridolin Peter Mechel''' (1930–2014) nach der '''Theorie der quasihomogenen Absorber''' mit der komplexwertigen Ausbreitungskonstante <math>\Gamma</math> näherungsweise angegeben werden. Die dimensionslose Hilfsgröße <math>\Omega</math> steht hierbei für das Verhältnis des Trägheitswiderstands der Luft <math>t = \rho_0 \cdot \omega</math> zum spezifischen Strömungswiderstand des porösen Absorbermaterials <math>r</math>, was ähnlich wie eine Reynolds-Zahl interpretiert werden kann: :<math>\Omega = \frac {t} {r} = \frac {\rho_0 \cdot \omega} {r}</math> :<math>\Gamma = \frac {i \omega} {c_0} \cdot \sqrt {1 - \frac {i} {\Omega}} = \frac {i \omega} {c_0} \cdot \sqrt {1 - i \cdot \frac {r} {\rho_0 \cdot \omega}}</math> :<math>A_{h,TL,D} = -A_0 (f) \cdot e^ {- \Gamma \cdot X_0} = -A_0 (f) \cdot e^ {-i \omega \cdot \frac {X_0} {c_0} \cdot \sqrt {1 - i \cdot \frac {r} {\rho_0 \cdot \omega}}}</math> Dabei ist <math>\rho_0</math> die Dichte des gasförmigen Schallmediums (bei trockener Luft bei 20°&nbsp;C ist <math>\rho_0 = 1.2 {\text{kg} \over \text{m}^3}</math>). Je größer der spezifische Strömungswiderstand, desto stärker ist die Schalldämpfung. Er hängt im Wesentlichen vom Material, von der Faserfeinheit und von der Massendichte <math>\rho</math>, also von der Packungsdichte, des Absorbers und vom Füllgrad in der Transmissionline ab. Weiterhin hängt er von der gleichmäßigen Verteilung des Absorbers aber auch von Faserrichtung und der Schallfrequenz ab, so dass in der folgenden Tabelle nur ungefähre Richtwerte für einige poröse, faserige Absorber angeben sind: {| class="wikitable zebra" | style="text-align:center" !Material !Massendichte !Spezifischer<br/>Strömungswiderstand !Gewichtseffizienz |- | |<math>\rho</math> in <math>\frac {\text{kg}} {\text{m}^3}</math> |<math>r</math> in <math>\frac {\text{Ns}} {\text{m}^4}</math> |<math>\eta_m = \frac {r} {\rho}</math> in <math>\frac {\text{Ns}} {\text{kg m}} = \text{Hz}</math> |- |Schafwolle |5 |4000 |800 |- |Schafwolle |10 |8000 |800 |- |Baumwolle |5 |1000 |200 |- |Baumwolle |10 |4000 |400 |- |Glaswolle |50 |200 |4 |- |Glasfaserplatten |20 |1000 |50 |- |Mineralwolle |10 |600 |60 |- |Mineralwolle |50 |10000 |200 |- |Aluminiumwolle |35 |500 |14,3 |- |Aluminiumwolle |70 |4000 |57,1 |} Die Gewichtseffizienz des Absorbermaterials <math>\eta_m</math> ist in der Tabelle als das Verhältnis des spezifischen Strömungswiderstands <math>r</math> zur Massendichte <math>\rho</math> angegeben: :<math>\eta_m = \frac {r} {\rho}</math> [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Laengen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Längen.]] [[Bild:Transmissionline.Lautsprecherbox.mit.verschiedenen.Daempfungen.gif|upright=2|miniatur|rechts|Amplitudenfrequenzgang einer Transmissionline-Lautsprecherbox mit verschiedenen Dämpfungen.]] Für die gesamte Transmissionline-Lautsprecherbox ergibt sich an der Front der Lautsprecherbox mit Dämpfung durch die Überlagerung des direkten Schalls mit dem Schall aus der gedämpften Transmissionline die folgende frequenzabhängige Amplitude: :<math>A_{ges,TL,D} = A_v + A_{h,TL,D} = A_0(f)\cdot \left(1 - e^{-i \omega \cdot {X_0 \over c_0} \cdot \sqrt{1 - i \cdot {r \over {\rho_0 \cdot \omega}}}}\right)</math> Der Amplitudenverlauf ergibt sich aus dem Betrag der komplexen Funktion <math>A_{ges,TL,D}</math>. Dieser muss entsprechend den Parametern Gesamtgüte des Tieftöners <math>Q</math>, Länge der Transmissionline <math>X_0</math> und spezifischer Strömungswiderstand <math>r</math> so angepasst werden, dass bei den tiefen Frequenzen ein möglichst gleichmäßiger Amplitudenverlauf entsteht. Die beiden nebenstehenden Abbildungen sollen den Einfluss der beiden Parameter <math>X_0</math> und <math>r</math> bei vorgegebenem <math>Q</math> verdeutlichen. Die als geeignet bestimmten Parameter können beim Bau einer Transmissionline-Lautsprecherbox umgesetzt werden. Insbesondere die richtige Bedämpfung muss hierbei jedoch experimentell überprüft und gegebenenfalls korrigiert werden. Ferner muss bei der Ansteuerung von Tief- und Mitteltöner die aus obiger Gleichung resultierende Amplitude :<math>|A_{ges,TL,D}|</math> und Phasendifferenz :<math>arg(A_{ges,TL,D})</math> bei der Übergangsfrequenz zwischen dem System aus Transmissionline und Tieftöner auf der einen Seite und Mitteltöner auf der anderen Seite mittels der Frequenzweiche ausgeglichen werden. <div style="clear:both"></div> === Erfahrungswerte für den Entwurf einer Transmissionline-Lautsprecherbox === [[Datei:Transmissionline-Lautsprecher.P1117437.jpg|mini|hochkant=1.5|1,2&nbsp;Meter hohe Transmissionline-Lautsprecherbox aus dem Jahr 1986 mit trapezförmiger Grundfläche (Breite vorne = 30&nbsp;Zentimeter, Breite hinten = 50&nbsp;Zentimeter, Tiefe = 27&nbsp;Zentimeter). Oben befindet sich der Tieftöner, darunter der Mitteltöner und unten der Hochtöner. Die beiden Austrittsöffnungen der unten im Gehäuse symmetrisch nach rechts und links aufgeteilten akustischen Transmissionlines befinden sich oben an den abgeschrägten Seiten hinter dem Tieftöner. Rechnerisch wird im Frequenzgang beim Unterschreiten von 30&nbsp;Hertz ein Pegelabfall von drei Dezibel erreicht. In einem größeren Abhöraum betrug die Pegelschwankung dieser Kombination aus Tieftöner und Transmissionline im Frequenzgang zwischen 20&nbsp;und 50&nbsp;Hertz ungefähr ein Dezibel.]] Der Querschnitt der Transmissionline direkt hinter dem Lautsprecher sollte mindestens so groß sein wie die Lautsprechermembranfläche oder sogar etwas darüber liegen. Entlang der Transmissionline kann sich deren Querschnitt um maximal 25&nbsp;Prozent verringern. Für den Transmissionline-Betrieb geeignete Tieftöner sollten eine relativ hohe Gesamtgüte <math>\left(Q \lessapprox 1\right)</math> haben. Mit einer passiven Frequenzweiche, über aktive Frequenzfilter beziehungsweise mit Hilfe von digitalen Signalprozessoren werden die elektrischen Signale auf die Tief- und Mittel- sowie gegebenenfalls auch die Hochtöner verteilt. Um unerwünschte Effekte durch akustische Interferenzen zu minimieren, sollten die seitlichen Abstände zwischen Mittel- und Tieftöner sowie zwischen Hoch- und Mitteltöner so gering wie möglich gehalten werden. Außerdem sollten alle Lautsprecher bündig mit dem Gehäuse abschließen und in einer Ebene liegen. Die Austrittsöffnung einer Transmissionline kann konstruktiv wie ein Subtieftöner betrachtet werden. Der Wert des spezifischen Strömungswiderstands <math>r</math> (und somit der Dichte <math>\rho_0</math>) sollte bei der fertiggestellten Lautsprecherbox experimentell überprüft (zum Beispiel mit Frequenzgenerator oder mit Rauschgenerator und Frequenzanalysator) und gegebenenfalls angepasst werden, da er für einen in die Transmissionline eingebauten Absorber meist nicht genau genug berechnet werden kann. Der Mess- oder Abhörraum hat durch seine Eigenfrequenzen im tiefen Frequenzbereich einen deutlichen Einfluss auf die Tieftonwiedergabe. Insbesondere bei parallelen Wänden (respektive Böden und Decken) können stehende Wellen Raumresonanzen hervorrufen, wenn die Schallwellenlänge <math>\lambda</math> dem Doppelten einer Raumdimension mit der Länge <math>s</math> entspricht: :<math>f_{Resonanz} = \frac {c_0} {\lambda} = \frac {c_0} {2 \cdot s}</math> In einem quaderförmigen Raum mit der Länge <math>l</math>, der Breite <math>b</math> und der Höhe <math>h</math> ergibt sich nach dem Satz der Pythagoras die kleinste Resonanzfrequenz aus der Raumdiagonale <math>d</math>: :<math>d = \sqrt {l^2 + b^2 + h^2}</math> :<math>f_{Resonanz, min} = \frac {c_0} {2 \cdot d} = \frac {c_0} {2 \cdot \sqrt {l^2 + b^2 + h^2}}</math> Ein quaderförmiger Raum mit einer quadratischen Grundfläche von 100&nbsp;Quadratmetern hat demnach eine niedrigste Eigenfrequenz von ungefähr 12&nbsp;Hertz. Durch schwere Vorhänge und Teppiche oder andere Schallabsorber kann die Schallreflexion in einem Raum deutlich reduziert werden, wobei die Schallabsorption für tiefer werdende Frequenzen in der Regel zunehmend geringer wird. Im freien Feld beziehungsweise in schallreflexionsarmen Messräumen entfallen solche Raumreflexionen oder sind so stark reduziert, so dass sich für einen gegebenen Lautsprecher ein anderes Klangbild als in einem gewöhnlichen Raum ergibt. {| class="wikitable" |+ Parameter der abgebildeten Transmissionline-Lautsprecherbox ! Parameter !! Formelsymbol !! Wert |- | Eigenresonanzfrequenz des Tieftöners || <math>f_0</math> || <math>\text {31 Hz}</math> |- | Güte des Tieftöners || <math>Q</math> || <math>\text {0,65}</math> |- | Länge der Transmissionline || <math>X_0</math> || <math>\text {2,75 m}</math> |- | Mittlerer spezifischer Widerstand der Dämmung mit Mineralwolle || <math>r</math> || <math>\text {1500 } \frac {\text {Ns}} {\text {m}^4}</math> |- | Mittlere Massendichte der Dämmung mit Mineralwolle || <math>\rho</math> || <math>\text {17,4 } \frac {\text {kg}} {\text {m}^3}</math> |- | Gewichtseffizienz der Dämmung mit Mineralwolle || <math>\eta_m</math> || <math>\text {86 } \text {Hz}</math> |} ==Widmung== Diese Zusammenstellung ist meinem Studienfreund '''Dr.&nbsp;Lutz König''' gewidmet. Der Hauptautor dankt ihm für seine freundschaftlichen, umfangreichen und stets förderlichen Beiträge zur Realisierung unserer 1986 in Eigenentwicklung hergestellten vier Lautsprecherboxen. == Literatur == * Arthur R.&nbsp;Bailey: ''A Non-resonant Loudspeaker Enclosure Design'' - ''Using acoustic transmission line with low-pass filter characteristics'', Wireless World, October 1965, p.&nbsp;483-486 * Arthur R.&nbsp;Bailey: ''The Transmission-line Loudspeaker Enclosure'' - ''A re-examination of the general principle and a suggested new method of construction'', Wireless World, May 1972, p.&nbsp;215-217 * {{w|Ludwig Bergmann (Physiker)|Ludwig Bergmann}}, {{w|Clemens Schaefer (Physiker)|Clemens Schäfer}}: ''{{w|Bergmann-Schaefer_Lehrbuch_der_Experimentalphysik|Lehrbuch der Experimentalphysik}}'', Band 1: ''Mechanik, Akustik, Wärme'', 9. Auflage, de Gruyter, 1974, ISBN 978-3-1100-4861-2 * Fridolin P.&nbsp;Mechel: ''Schallabsorption'', Kapitel&nbsp;18, in: {{w|Manfred Heckl}}, {{w|Helmut A. Müller (Akustiker)|Helmut A.&nbsp;Müller}}: ''Taschenbuch der technischen Akustik'', Springer-Verlag, Berlin, Heidelberg, New York, 1975, ISBN 3-6429-7357-4 * Hans Herbert Klinger: ''Lautsprecher und Lautsprechergehäuse für HiFi'', Franzis-Verlag GmbH, München, 1981, ISBN 3-7723-1051-6 * Heinz Sahm: ''HiFi-Lautsprecher'', ''Grundlagen der elektrodynamischen Lautsprecher in unendlicher Schallwand und im Gehäuse'', Franzis-Verlag GmbH, München, 1982, ISBN 3-7723-6522-1 * Berndt Stark: ''Lautsprecher-Handbuch'' - ''Theorie und Praxis des Boxenbauens'', Richard Pflaum Verlag, München, 1985, ISBN 3-7905-0433-5 == Siehe auch == * [[Grundlegendes zur Akustik]] == Weblinks == {{Commonscat|Transmission line loudspeakers}} == Zusammenfassung des Projekts == {{Vorlage:StatusBuch|10}} * '''Zielgruppe und Lernziele:''' Dieses Buch richtet sich an akustisch interessierte Leser mit mathematischer Ausbildung. Es beschreibt einen vollständigen Weg zur Berechnung einer bedämpften akustischen Transmissionline mit komplexwertigen Funktionen. Aufbauend auf grundlegenden Betrachtungen zur Schallerzeugung und -abstrahlung von Lautsprechern wird durch schrittweise Erweiterungen und Ergänzungen die Funktionsweise einer solchen Transmissionline erarbeitet. * '''Ansprechperson:''' Ansprechpartner ist [[Benutzer:Bautsch]] [[Kategorie:Geometrische Kuriositäten‎]] gqbytib18xkci9afc7ynejfym4101sj Astronomie von der Frühgeschichte bis zur Neuzeit/ Die Plejaden 0 114816 1088126 1087067 2026-06-14T00:57:48Z Bautsch 35687 /* Wanderung des Frühlingspunkts */ Schaltrechnung 1088126 wikitext text/x-wiki Die '''Plejaden''' gehören mit dem Mond und den fünf mit bloßem Auge sichtbaren Planeten, zu den auffälligsten Objekten am Nachthimmel, die ohne Hilfsmittel beobachtet werden können. ==Die Sterne der Plejaden== [[Datei:Hyaden.P10916XX.png|mini|rechts|hochkant=2|Der offene Sternhaufen der '''Hyaden''' mit dem hellen Roten Riesen Aldebaran (α&nbsp;Tauri).]] Die Plejaden bestehen aus insgesamt über eintausend einzelnen Sternen. Sie gehören zu einem offenen Sternhaufen und sind unserem Sonnensystem zwar nicht so groß und so nah wie der ebenfalls im Sternbild Stier (Taurus) gelegene Sternhaufen der nur 153 Lichtjahre entfernten '''Hyaden''', mit einer Entfernung von rund 400 Lichtjahren sind sie dennoch nah genug, dass einzelne der helleren Sterne mit bloßem Auge unterschieden werden können. Diese beiden Sternhaufen bilden das [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/ Astronomische Bezugssysteme#Das Goldene Tor der Ekliptik|'''Das Goldene Tor der Ekliptik''']], durch das im Laufe der Zeiten immer wieder alle Wandelgestirne hindurchziehen. <gallery caption="Der offene Sternhaufen der Plejaden" widths=480 heights=360 mode="packed"> Plejaden.Sternenpark.Westhavelland.P1022928.jpg|Zum Frühlingsbeginn am Abendhimmel im Internationalen Sternenpark Westhavelland. Sidereus nuncius figura10.png|Darstellung im Siderius Nuncius ("Sternenbote") nach der Fernrohrbeobachtung von Galileo Galilei vom März 1610. Plejaden.2.1.2020.21.10.Uhr.P1044869.jpg|Die Hauptsterne am Abendhimmel Anfang Januar in Berlin. Plejaden.Animation.plus.gif|mini|hochkant=2|Mit den Namen der Hauptsterne. </gallery> [[Datei:Sternschnuppe.scheinbare.Helligkeit.P1091635.png|mini|rechts|hochkant=2|Beispiel einer Sternschnuppe mit einer Spurlänge von zwei Bogengrad vom Sternbild Stier (Taurus, rechts oben) durch das Sternbild Eridanus (Mitte) bis in das Sternbild Orion (links) im Vergleich zu benachbarten Sternen vierter bis neunter Größenklasse im Vergleich.<br />Die folgenden Sterne sind mit ihrer scheinbaren Helligkeit gekennzeichnet:<br />4<sup>m</sup> = μ&nbsp;Eridani (mit farbiger Szintillation)<br />5<sup>m</sup> = c&nbsp;Eridani<br />6<sup>m</sup> = HIP&nbsp;21718&nbsp;Eridani<br />7<sup>m</sup> = HS&nbsp;Eridani<br />8<sup>m</sup> = HIP&nbsp;22270&nbsp;Orionis<br />9<sup>m</sup> = HIP&nbsp;22316&nbsp;Orionis.]] :'''Anmerkung:''' ''Die numerische Größenklasse der scheinbaren Helligkeit wird durch ein nach- und hochgestelltes m (für magnitudo beziehungsweise kürzer auch mag) gekennzeichnet. Eine um eine Größenklasse höhere Zahl, bedeutet eine Abnahme der scheinbaren Helligkeit um einen Faktor von rund 2,5. Der Helligkeitsunterschied zwischen dem hellsten Stern des Nachthimmels Sirius (-1,5<sup>m</sup>) und den dunkelsten, gerade noch mit unbewaffnetem Auge sichtbaren Sternen (6<sup>m</sup>) entspricht demzufolge einem Verhältnis von 1000 zu 1. Die scheinbare Helligkeit sagt nichts über die Größe, Entfernung oder absolute Helligkeit eines Sternes aus.'' Je nach Restlicht in der Dämmerung und je nach Höhe über dem Horizont können erst nur der hellste Stern dieses Sternhaufens, Alkione, und dann bis zu zwölf Sterne erkannt werden, von denen sieben eine scheinbare Helligkeit bis zur fünften Größenklasse erreichen. Diese sieben Sterne sind in dunklen Nächten ohne Lichtverschmutzung besonders gut zu unterscheiden und haben den Begriff "Siebengestirn" (akkadisch "Šebettu", griechisch "heptasteros", althochdeutsch "sibunstirri") geprägt. Atlas ist in der griechischen Mythologie der Vater und Pleione die Mutter der sieben Plejaden. Die Ausdehnung der mit bloßem Auge sichtbaren Sterne der Plejaden beträgt ungefähr ein Bogengrad von Atlas und Pleione zu Taygeta, Celaeno und Electra sowie zirka ein halbes Bogengrad von der Linie Merope–Electra zur Linie Pleione–Asterope. Der Winkelabstand zwischen den beiden paarweise am nächsten gelegenen Sternen Pleione und Atlas beträgt 15&nbsp;Bogenminuten. Zwei leuchtende Punkte können bei diesem Abstand bei guten Sichtverhältnissen auch freiäugig ohne weiteres unterschieden werden. Die hellsten sieben Hauptsterne der Plejaden sind im Folgenden aufgelistet: {| class="wikitable" |+ Die sieben Hauptsterne der Plejaden !title="Eigenname"|Eigenname !title="Scheinbare Helligkeit"|Scheinbare<br/>Helligkeit !title="Minimale Horizonthöhe für die freiäugige Sichtbarkeit"|Minimale Horizonthöhe<br/>für die freiäugige Sichtbarkeit |- | Alkione || 3,0<sup>m</sup> || 4° |- | Atlas || 3,5<sup>m</sup> || 5° |- | Electra || 3,5<sup>m</sup> || 5° |- | Maia || 4,0<sup>m</sup> || 6° |- | Merope || 4,0<sup>m</sup> || 6° |- | Taygeta || 4,0<sup>m</sup> || 6° |- | Pleione || ≈5,0<sup>m</sup> || 10° |} → Siehe auch [[Quadriviale Kuriositäten‎/ Zahlen#Zur Sieben|Exkurs '''Zur Sieben''']]. Zwei weitere, etwas dunklere Sterne des Sternhaufens haben die Eigennamen der beiden anderen Plejaden aus der Mythologie, nämlich ''Celaeno'' (5,5<sup>m</sup>) und ''Asterope'' (6,0<sup>m</sup>). Celaeno ist somit erst ab einer Horizonthöhe von 16&nbsp;Bogengrad zu sehen und Asterone sogar erst am ungefähr 25&nbsp;Bogengrad. Über diese neun genannten Sterne hinaus gibt es noch zwei weitere Sterne am Rand des offenen Sternhaufens, die mit bloßem Auge gesehen werden können, nämlich 18 Tauri (5,6<sup>m</sup>) am nördlichen Rand und HD 23753 (5,4<sup>m</sup>) am südlichen Rand. Der nächstdunklere Stern ist sehr nah bei Asterope der Stern Sterope&nbsp;II (22&nbsp;Tauri) mit einer scheinbaren Helligkeit von (6,4<sup>m</sup>), der unter optimalen Bedingungen zumindest theoretisch noch ohne Fernrohr gesehen werden könnte, wenn die Plejaden in der Nähe des Zenits stehen. Alle weiteren Sterne der Plejaden sind deutlich dunkler und mit bloßem Auge somit nicht sichtbar. Für den Sternhaufen resultiert insgesamt eine scheinbare Helligkeit von ungefähr 1,5<sup>m</sup>. ==Sichtbarkeit== Die Plejaden stehen heute sowohl am 20.&nbsp;Mai (in Konjunktion zur Sonne sind sie dann unsichtbar) als auch am 18.&nbsp;November (in Opposition zur Sonne und um Mitternacht mit maximaler Höhe über dem südlichen Horizont) im Meridian. Der Meridian ist der gedachte Großkreis, der sowohl durch die beiden Himmelspole als auch durch den Zenit und den Nadir läuft. Im Winter und im Frühjahr sind die Plejaden am Abendhimmel in westlicher Richtung und im Sommer und im Herbst am Morgenhimmel in östlicher Richtung zu beobachten. [[Datei:Plejaden.Abendletzt.P1138744.jpg|mini|rechts|hochkant=2|Die Plejaden beim Abendletzt (akronychischer Untergang) von Berlin aus gesehen. Die effektive scheinbare Helligkeit zu Beginn der nautischen Abenddämmerung betrug 3,7<sup>m</sup> (Alkione) bis 6,4<sup>m</sup> (Celaeno), die Höhe über dem nordwestlichen Horizont 8&nbsp;Bogengrad.]] Seit jeher hatten die Auf- und Untergänge der Plejaden eine hohe kulturelle und wissenschaftliche Bedeutung. Heliakische Aufgänge sind hierbei "zur Sonne gehörend", also in Nähe zur aufgehenden Sonne (Morgenerst), und akronychische Untergänge befinden sich "am Rand der beginnenden Nacht", also in Nähe zur untergehenden Sonne (Abendletzt). Für die Beobachtung der Plejaden muss die Sonne allerdings unter dem Horizont stehen, und der Abstand zur Sonne (also die Elongation) muss mehr als 18&nbsp;Bogengrad betragen, damit das in der Atmosphäre gestreute Sonnenlicht die Sterne des Sternhaufens nicht überstrahlt. Die akronychischen Aufgänge (Abenderst) sowie die heliakischen Untergänge (Morgenletzt) spielen für Fixsterne (und somit auch für die Plejaden) keine Rolle, da diese im Gegensatz zum Mond, zu den Planeten und zu Kometen in den Nächten zwischen Morgenerst und Abendletzt immer zu sehen sind. Um 2320&nbsp;vor Christus befanden sich die Plejaden genau auf der ekliptikalen Länge des Frühlingspunkts und der akronychische Untergang fand also genau zur Tag-und-Nacht-Gleiche, dem Julianischen Datum zufolge zirka 20&nbsp;Tage vor dem heutigen Frühlingsbeginn also am Anfang des Monats März statt, der in alten Sonnenkalendern der erste Monat des Jahres war. Um 1000&nbsp;vor Christus hatten die Plejaden eine ekliptikale Länge von rund 18&nbsp;Bogengrad, so dass der akronychische Untergang nach dem Julianischen Datum um den 21.&nbsp;März erfolgte. Der Zeitpunkt des heliakischen Aufgangs der Plejaden in Bezug auf die durch die Mondphasen bestimmten zwölf Monate machte diese im babylonischen Lunisolarkalender zu einem Kalendergestirn. Wenn der Aufgang sich bis in den dritten Kalendermonat (Simanu) verschoben hatte, wurde ein dreizehnter Schaltmonat eingelegt, womit die Kalendermonate wieder mit dem Frühlingsbeginn des Sonnenjahrs synchronisiert werden konnten. Auch die neuseeländischen Māori orientierten sich am heliakischen Aufgang der Plejaden, um den Termin des Neujahrs festzulegen und mit der Aussaat zu beginnen. Vor 5000&nbsp;Jahren gingen die Plejaden auf der Linie des Horizonts ungefähr bei 7&nbsp;Bogengrad nördlich der Ekliptik auf und bei 4&nbsp;Bogengrad nördlich der Ekliptik unter. Heute gehen die Plejaden auf der Linie des Horizonts fast unverändert ungefähr bei 7&nbsp;Bogengrad nördlich der Ekliptik auf und bei 5&nbsp;Bogengrad nördlich der Ekliptik unter. Durch die damalige Lage der Ekliptik gingen die Plejaden überall auf der Erde und immer im Westen (bei einem Azimut von rund 270&nbsp;Bogengrad) unter und im Osten (bei einem Azimut von rund 90&nbsp;Bogengrad) auf. Im gegenüberliegenden Punkt der Himmelssphäre befand sich der sehr auffällige Rote Überriese Antares (α&nbsp;Scorpii) im Sternbild Skorpion (Scorpio). Sowohl die Plejaden als auch Antares waren gleichzeitig zu sehen (die sogenannte "Plejaden-Waage"), wenn sie beide ein Bogengrad über dem Horizont standen. Sie waren deswegen während der dunklen Jahreszeiten zur direkten Bestimmung dieser ausgezeichneten Himmelsrichtungen geeignet. Vom Elsässer '''Bel'''chen aus gesehen gehen die Plejaden heute beispielsweise immer über dem Kleinen '''Bel'''chen auf, wo auch die Sonne bei der Sommersonnenwende aufgeht. Am 1.&nbsp;Mai, also an dem Tag, an dem die Plejaden in unserer heutigen Zeit in der maltesischen Abenddämmerung verschwunden sind, geht sie genau über dem höchsten Berg der Vogesen, dem Großen '''Bel'''chen auf. Dieser wurde vermutlich dem keltischen Lichtgott '''Bel'''enus geweiht, dessen Feiertag '''Bel'''tane auf den 1.&nbsp;Mai fällt. Der Schwarzwälder '''Bel'''chen befindet sich exakt in östlicher Richtung, also auf dem gleichen Breitengrad wie der Elsässer Belchen (47,82° nördliche Breite). An den beiden Tagen der Tag-und-Nacht-Gleiche beim Frühlings- und Herbstanfang gehen Himmelsobjekte, die sich in der Nähe des Frühlings- beziehungsweise des Herbstpunktes der Sonne befinden (also auch die Plejaden, die sich vor 5000&nbsp;Jahren dort befanden), vom Elsässer Belchen aus gesehen genau im Osten über dem Schwarzwälder Belchen auf beziehungsweise vom Schwarzwälder Belchen aus gesehen genau im Westen über dem Elsässer Belchen unter.<ref>Walter Eichin und Andreas Bohner: [http://dl.ub.uni-freiburg.de/diglit/mgl-1985-02/0178 Das Belchen-System], Universitätsbibliothek Freiburg im Breisgau, in: ''Das Markgräflerland: Beiträge zu seiner Geschichte und Kultur'', 47, 1985, Heft 2, Seiten 176 bis 185</ref> → Siehe auch '''[[Das Belchen-System]]'''. ==Als Bezugspunkt== [[Datei:Ekliptik.helle.Objekte.png|mini|hochkant=2|rechts|Die sieben hellsten feststehenden Himmelsobjekte in der Nähe der Ekliptik liegen zwischen den Sternbildern Stier (Taurus, rechts) und Skorpion (Scorpio, links). Zwischen den beiden offenen Sternhaufen der '''Hyaden''' und der Plejaden befindet sich das '''Goldene Tor der Ekliptik'''. Der Bogen der Ekliptik wird von den Wandelgestirnen vom Frühlingspunkt rechts zum Herbstpunkt links durchlaufen. In der unteren Hälfte der Ekliptik befinden sich keine hellen, ekliptiknahen Fixsterne. Außerhalb des Bogens liegende Punkte befinden sich nördlich der Ekliptik und innen liegende südlich. Der hellste gegenüber von Regulus liegende Stern '''Fomalhaut''' (α Piscis Austrini) im Sternbild Südlicher Fisch (Piscis Austrinus) ist zur Vervollständigung und zur Orientierung hinzugefügt, obwohl er sich mehr als zwanzig Bogengrad südlich der Ekliptiklinie befindet.]] Die '''Plejaden''' liegen nahe der Ekliptik und sind eines der hellsten und aufgrund ihrer Form das auffälligste Fixsternobjekt an der Ekliptik. Sie sind ebenso wie die Hyaden ein "Pfosten" des [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Das_Goldene_Tor_der_Ekliptik|'''Goldenen Tors der Ekliptik''']]. Um 2300&nbsp;vor Christi Geburt lag der Frühlingspunkt auf der Ekliptik bei der gleichen ekliptikalen Länge wie das Siebengestirn im heutigen Sternbild Stier (Taurus). Die Plejaden haben eine nördliche ekliptikale Breite von rund vier Bogengrad. Damit können sie unter Berücksichtigung der möglichen geographischen Breiten der Beobachtung von den Wandelgestirnen bedeckt werden, deren Bahnen eine hinreichend große Neigung zur Ekliptik haben.<ref name="Papke">Werner Papke: ''Zwei Plejaden-Schaltregeln aus dem 3.&nbsp;Jahrtausend'', Archiv für Orientforschung, 31. Band, 1984, Seiten 67-70</ref> Dies sind der Mond (Bahnneigung gut 5&nbsp;Bogengrad), die Venus (Bahnneigung 3,4&nbsp;Bogengrad) und der Merkur (Bahnneigung rund 7&nbsp;Bogengrad). Letzterer ist wegen seiner permanenten Sonnennähe allerdings mit bloßem Auge nie gleichzeitig mit den Plejaden zu sehen. Bis heute hat sich der Frühlingspunkt gut 60&nbsp;Bogengrad in westlicher Richtung verschoben, so dass er über das heutige Sternbild Widder (Aries) in das heutige heute Sternbild Fische (Pisces) weitergewandert ist. Der Roten Riesen '''Aldebaran''' (α Tauri) ist innerhalb der Hyaden zu sehen, gehört selbst jedoch nicht zu diesem Sternhaufen. Sein Eigenname stammt vom arabischen Wort "al-dabaran" ab, was so viel wie "der Nachfolger" beziehungsweise "der Verfolger" bedeutet. Kurz nach dem Aufgang der Plejaden erscheint er ebenfalls über dem östlichen Horizont und scheint den Sternhaufen auf dem Bogen nach Westen stets zu Verfolgen. '''Aldebaran''' (α Tauri) und der Rote Überriese '''Antares''' (α Scorpii) liegen fast auf der Ekliptik und unterscheiden sich in ihrer ekliptikalen Länge um fast genau 180&nbsp;Bogengrad. Die beiden äußersten Pole der Reihe hellsten feststehenden Himmelsobjekte in der Nähe der Ekliptik, der Stern Antares und der Sternhaufen der Plejaden, werden in ihrer Eigenschaft als Kalendergespann auch als '''Plejaden-Waage''' bezeichnet.<ref name="ErnstVonBunsen">Ernst von Bunsen: ''Die Plejaden und der Thierkreis oder: Das Geheimnis der Symbole'', Verlag von Mitscher und Röstell, Berlin, 1879</ref> Für die Menschen waren die beiden sehr hellen und rot leuchtenden Sterne Antares im Sternbild Skorpion (Scorpio) und Aldebaran im offenen Sternhaufen der Hyaden mit dem gegenüberliegenden Siebengestirn im Sternbild Stier (Taurus) im Altertum ein Gespann, mit dem auf einfache Weise die Zeitpunkte des Frühlings- und des Herbstanfangs im Sonnenjahr zuverlässig bestimmt werden konnten. Der in Abbildung zu sehende obere Halbbogen der Ekliptik befand sich damals zum Frühlingsbeginn bei Sonnenuntergang und zum Herbstbeginn bei Sonnenaufgang vollständig oberhalb des Horizonts. Zum Sommerbeginn war dieser Halbbogen um Mitternacht vollständig unter dem Horizont und daher gar nicht zu sehen. Der sichtbare Teil der Ekliptik war zum Winterbeginn um Mitternacht vom Stern Antares Osten bis zu den Plejaden im Westen vollständig und fast gleichmäßig in 45-Grad-Schritten durch die weitern angegebenen drei ekliptiknahen Sterne '''Spica''' im Sternbild Jungfrau (Virgo), '''Regulus''' im Sternbild Löwe (Leo) und '''Pollux''' im Sternbild Zwillinge (Gemini) markiert. Die Ekliptik schnitt vor 4300&nbsp;Jahren bei maximaler Höhe und nahe dem Stern Regulus den südlichen Meridian. Regulus stand also zum Frühlingsbeginn bei Sonnenuntergang, zur Sommersonnenwende mittags, zum Herbstbeginn bei Sonnenaufgang und zur Wintersonnenwende um Mitternacht hoch im Süden. → Siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Die_Ekliptik|'''Die Ekliptik''']]. → Siehe auch [[Quadriviale Kuriositäten‎/ Zahlen#Die sieben hellsten Objekte der Ekliptik|'''Die sieben hellsten Objekte der Ekliptik''']]. ==Darstellungen im Altertum== Bei den Plejaden handelt es sich um einen äußerst auffälligen Asterismus in der Nähe der Ekliptik, und sie sind daher praktisch von jedem Ort der Erde viele Monate lang in der Nacht zu sehen. Die einzelnen Sterne können vom Mond bedeckt werden, und so ist es nicht verwunderlich, dass ihnen zu allen Zeiten und an allen Orten eine besondere Bedeutung und Aufmerksamkeit am Sternenhimmel zugeordnet wurde. Es wird diskutiert, ob die Plejaden innerhalb des Sternbilds Stier (Taurus) bereits in den steinzeitlichen Zeichnungen in der '''Höhle von Lascaux''' dargestellt sind.<ref>Dirk Lorenzen: [https://www.deutschlandfunk.de/astronomie-astronomie-in-der-hoehle.732.de.html?dram:article_id=329579 Astronomie in der Höhle], Deutschlandfunk, 11. September 2015</ref><ref>[https://www.scinexx.de/dossierartikel/sternenkarten-in-der-eiszeithoehle/ Sternenkarten in der Eiszeithöhle – Astronomie in den Höhlenmalereien von Lascaux?], scinexx, 1. Februar 2008</ref> In der neolithischen '''Magura-Höhle''' in Bulgarien tauchen bei den Höhlenmalereien zum Beispiel sehr viele Figuren mit zum Himmel erhobenen Armen auf.<ref name="Magura">Kiril Kirilov: [https://magnaaura.wordpress.com/2017/06/29/the-origin-of-civilizations-according-to-the-prehistoric-paintings-of-magura-cave/ The origin of civilizations according to the prehistoric paintings of Magura cave], 29. Juni 2017</ref> Viele Figuren ähneln deutlich dem heutigen Sternbild Orion. An der Wand eines Korridors gibt es eine mythisch anmutende Gruppe mit einer Siebengestalt. In einer anderen größeren Zusammenstellung sind in der oberen Hälfte zahlreiche Gestalten mit erhobenen Händen zu erkennen, wohingegen darunter eher eine irdische Szene mit Menschen und Tieren zu sehen ist. Auch mehrere sonnen- und mondartige sowie stierartige und stierkopfartige Figuren sind in der Nachbarschaft dieser Darstellungen zu erkennen. Es ist daher eine naheliegende Annahme, dass die erwähnte Siebengestalt die Plejaden oder vielleicht auch die sieben Wandelgestirne symbolisieren könnte. Die Plejaden sind vermutlich auf der '''Himmelsscheibe von Nebra''' aus der frühen Bronzezeit (um 2000 vor Christus) als sieben goldene Scheibchen abgebildet. Auch das 1891 in Allach bei München gefundene '''keltische Eisenschwert''' aus dem dritten Jahrhundert vor Christus ist mit goldenen Tauschierungen ausgeführt, die die Plejaden zeigen.<ref>Peter Kurzmann: [https://www.archaeologie-online.de/artikel/2014/plejaden-auf-einem-keltischen-schwert/ Die Plejaden in Gold auf einem keltischen Schwert], Archäologische Informationen 39, 2016, 239-246</ref> [[Datei:Knowth.Kerbstone15.Pleiades.png|mini|rechts|Sieben Kreise an der Kante des Randsteins 15 vom neolithischen Hauptgrabhügel Knowth in Irland.]] Eine sehr ähnliche Darstellung findet sich auf einem Randstein des steinzeitlichen '''Ganggrabs Knowth''' im irländischen Boyne Valley in der Nähe von Newgrange. Der Hauptgrabhügel ist rund 5100&nbsp;Jahre alt, etwa 12&nbsp;Meter hoch und hat einen Durchmesser von 67&nbsp;Metern. Er enthält zwei in Ost-West-Richtung verlaufende Gänge, die ursprünglich von 127&nbsp;Randsteinen umgeben waren, von denen 124&nbsp;noch erhalten sind. Auf der ebenen Oberfläche von Randstein&nbsp;15 (kerbstone&nbsp;15), der sich am östlichen Rand der Anlage befindet, ist in der Mitte möglicherweise eine Sonnenuhr dargestellt, und am Rand des Steins taucht eine Darstellung aus sieben Kreisen auf.<ref>Euan W. MacKie: [https://www.google.de/books/edition/Professor_Challenger_and_his_Lost_Neolit/x1wwEAAAQBAJ?hl=de&gbpv=1&dq=knowth%20ireland%20kerbstone%2015&pg=PA110&printsec=frontcover Professor Challenger and His Lost Neolithic World: The Compelling Story of Alexander Thom and British Archaeoastronomy], Archaeopress Publishing Limited, Februar 2021, ISBN 9781784918347</ref><ref>Euan W. MacKie: [https://theorkneynews.scot/wp-content/uploads/2020/11/TM.pdf The Prehistoric Solar Calendar: An Out-offashion Idea Revisited with New Evidence], in: ''Time and Mind: The Journal of Archaeology, Consciousness and Culture'', Band 2, Ausgabe 1, March 2009, Seiten 9 bis 46</ref> In Mesopotamien sind die Plejaden auf mehreren aus Ton gefertigten assyrischen MUL.APIN-Keilschrifttafeln der '''Astrolab&nbsp;B Kalender''' verzeichnet (siehe auch unten [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Plejaden#Schaltregeln|Abschnitt „Schaltregeln“]]). Auch auf dem bronzezeitlichen '''Diskos von Phaistos''' von der Insel Kreta taucht 17-mal ein kreisförmiges Symbol mit sieben innenliegenden Punkten auf, das mit den Plejaden in Verbindung gebracht wurde: [[Datei:Diskos.von.Phaistos.Siebenerkreissymbol.png|60px]]<ref>Angelika Merk-Schäfer: [https://drmerkschaefer.files.wordpress.com/2016/10/der-diskos-von-phaistos-1-20062015.pdf Der Diskos von Phaistos - ein Venus- und Mondkalender im Kontext der minoischen Altpalastzeit auf Kreta. Die mit Symbolen gestempelte Scheibe aus gebranntem Ton ist höchstwahrscheinlich ein Agrar- und Ritualkalender im Dienste der Mond- und Venus-Gottheiten im minoischen Kreta.], drmerkschaefer.files.wordpress.com, Juni 2015</ref> Seit mehreren Jahren wird vermutet, dass auch auf dem zirka zwei Meter großen Stein mit becherförmigen Vertiefungen (französisch: "roche à cupules") auf der Hexenebene (französisch: "Plan des Sorcières") in der Gemeinde Lillianes im Aostatal die Plejaden abgebildet sind.<ref>[http://www.archeosvapa.eu/lillianes-plan-des-sorcieres-roche-a-cupules/ Lillianes Plan des Sorcières, roche à cupules], La Società valdostana di Preistoria e Archeologia</ref> Ähnliche Vermutungen gibt es für eine Anordnung von sieben Löchern beim '''Kalenderstein von Leodagger''' in Niederösterreich.<ref>Irene Hager, Hans Katzgraber, Karl Aigner, Stefan Borovits, Ernst Bellant: ''Die Darstellung von (konkreten oder symbolischen?) Himmelsobjekten auf dem Plateau des Kalendersteins in Leodagger (Niederösterreich)'', in: ''Himmelswelten und Kosmovisionen, Imaginationen, Modelle, Weltanschauungen'', Abstractbook 2019, Seite 5 und 6, Gesellschaft für Archäoastronomie, Wien</ref> <gallery caption="Altertümliche Darstellungen mit mehreren unregelmäßig angeordneten Gestirnen" mode=packed widths=360 heights=360> LascauxHimmelsstier.png|Stierkörper in der Höhle der Stiere von Lascaux in Frankreich. Über dem Rücken befinden sechs Punkte sich an der Stelle, wo sich im Himmelsstier die Plejaden befinden. Lillianes,_pietra_con_7_coppelle.JPG|Ungefähr zwei Meter großer Stein in der Nähe des Ortes Lillianes im Aostatal in Norditalien, dessen ungefähr 4200 vor Christi Geburt hergestellte Mulden mit den Plejaden identifiziert wurden.<ref>[https://www.stonepages.com/news/archives/002663.html The Pleiades carved by prehistoric people in the Alps], ANSA, Virgilio Notizie, 12 January 2008</ref> 2019-06-07_06-22_Irland_131_Brú_na_Bóinne,_Knowth,_Megalithic_Tombs.jpg|Randstein&nbsp;15 (kerbstone&nbsp;15) am östlichen Rand des steinzeitlichen Ganggrabs Knowth in Irland. Siebengestalt.Magura.Korridor.png|mini|Die Siebengestalt an der dritten Station der neolithischen Magura-Höhle in Bulgarien. Siehe auch [[Die Höhlenmalerei in der Magura-Höhle#Dritte Station|Wikibook '''Die Höhlenmalerei in der Magura-Höhle''' / Abschnitt '''Dritte Station''']]. Plejaden.Mnajdra.stele.png|Acht Näpfchen an der linken Seite einer Stele im kleinen Tempel von Mnajdra, Malta<ref>Umzeichnung nach der Filmszene von Erwin Wiedergrüsser: ''Licht und Steine - Maltas Tempel zur Wintersonnenwende'', YouTube, 9:00 Minuten bis 10:04 Minuten, 24. Januar 2016</ref> Himmelstafel.Tal-Qadi.Plejaden.png|Die Darstellung von sieben sternartigen Symbolen im vierten Segment der Himmelstafel von Tal-Qadi. Siehe auch [[Die_Himmelstafel_von_Tal-Qadi#Halbrechtes_Segment_(4)|Wikibook '''Die Himmelstafel von Tal-Qadi''' / Abschnitt '''Halbrechtes Segment (4)''']]. Rollsiegel.Plejaden.neuassyrisch.Qalat.Scherqat.Chalzedon.VA10114.P1068108.jpg|Neuassyrisches Rollsiegel mit Plejaden aus Qal'at Scherqat, Material Chalzedon, Neues Museum Berlin, VA10114. Upper_part,_Tell_el-Rimah_Stele_of_Adad-nirari_III_at_the_Iraq_Museum_in_Baghdad,_Iraq.jpg|Oberer Bereich der Stele aus Tell al Rimah vom assyrischen König Adad-nīrārī III. (Regierungszeit um 800 vor Christi Geburt) im Irakischen Nationalmuseum in Bagdad. Dieser betet unter den acht symbolisierten Gottheiten Sibitti (Plejaden), Ishtar (Venus), Sin (Mond) auf der linken Seite sowie Anu oder Enlil (Hörnerkrone), Assur (Flügelsonne), Nabu (Schreibgriffel), Marduk (Spaten) und Adad (Blitzbündel) auf der rechten Seite.<ref>Friedhelm Pedde: ''[https://wfs.berlin/wp-content/uploads/2022/01/14-BROplanet-februar2022ff_www.pdf Götter und Planeten im Alten Orient – Die Sterne und ihre Götter]'', Mitteilungen, Ausgabe 13, Seite 7, Februar 2022, Wilhelm-Foerster-Sternwarte e.V. / Zeiss-Planetarium am Insulaner</ref> Tontafel.VAT07851.MUL.MUL.P1068094.jpg|Seleukidischen Ritzzeichnung auf einer Tontafel (VAT 7851, Vorderasiatisches Museum in Berlin), auf der die Plejaden explizit mit sieben Sternen dargestellt und mittig mit Keilschriftzeichen beschriftet sind. Die Beschriftung [[Datei:MUL.MUL.png|120px]] ("MUL.MUL") kommt aus dem Sumerischen und bedeutet "Plejaden" (wörtlich übersetzt "Sterne"). </gallery> <gallery caption="Altertümliche Darstellungen von sieben regelmäßig angeordneten Gestirnen" mode=packed widths=360 heights=360> Himmelsscheibe.Nebra.vorne.P1034154.jpg|Die Himmelsscheibe von Nebra mit einer auffälligen Anhäufung von sieben goldenen Scheibchen. Diskos_von_Phaistos_(Seite_A)_11.jpg|Die eine Seite des Diskos von Phaistos zeigt vierzehn gestempelte kreisförmige Symbole mit sieben innenliegenden Punkten. Leodagger_Näpfchen.jpg|Sieben Näpfchen im Kalenderstein von Leodagger, Österreich. </gallery> ==Überlieferungen== Die Plejaden sind das auffälligste Objekt im Asterismus des [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Der_Himmelsstier|Himmelsstieres]] und auch des heutigen Sternbilds Stier (Taurus). Die Bezeichnung kommt aus dem Altgriechischen, es ist jedoch nicht eindeutig geklärt, was die ursprüngliche Bedeutung ist. Hierzu wird häufig das altgriechische Lehnwort πλείας (Nominativ Singular: "pleias", Genitiv, Singular: "pleiados", Nominativ Plural: "pleiades") für "Schiffer" herangezogen, weil sie als Himmelszeichen für den Beginn der weniger gefährlicheren Schiffbarkeit des Mittelmeers genutzt worden sind.<ref>Wübbe Ulrich Jütting: ''Phonetische, Etymnologische und Orthographische Essays über Deutsche und Fremde Wörter mit Harten und Weichen Verschlusslauten'', Seite 266, Verlag R. Herrosé, Gräfenhainichen / Wittenberg, 1884</ref> In auffällig vielen Sprachen werden Deminutive (Verkleinerungsformen) oder Attribute wie "klein" verwendet, um die Plejaden zu benennen. Der Bezug auf den altgriechischen Komparativ πλείων ("pleion") mit der Bedeutung "zahlreicher" ist ebenfalls nicht abwegig, denn in einem Sternhaufen sind die Sterne zahlreicher als bei einem einzelnen Stern. Diese Bedeutung taucht bei Begriffen wie "Haufen", "Versammlung" oder "Reichliche" sinngemäß auf. Ferner wurden wegen der Funktion als Kalenderstern auch die Ableitung vom altgriechischen Wort πλείων ("pleion") mit der Bedeutung "Jahr" beziehungsweise "Jahreszeit" sowie wegen der gelegentlich gebräuchlichen Bezeichnung als "Taubengestirn" auch der Ursprung aus πελειαδες ("pleleiades" = "Tauben") diskutiert.<ref>Johann Evangelista Rivola: ''Ueber die griechischen Sternbilder insbesondere die Plejaden'', Astronomisch-mythologische Abhandlung, Seite 27, Verlag Malsch und Vogel, Karlsruhe, 1858</ref> Die Plejaden werden im Deutschen auch '''Siebengestirn''' genannt, was den unmittelbaren Bezug zur magischen, mystischen und göttlichen Zahl Sieben herstellt.<ref>Ferdinand Freiherr von Andrian-Werburg: ''Die Siebenzahl im Geistesleben der Völker'', in: ''Mittheilungen der Anthropologischen Gesellschaft in Wien'', Band 31, Seiten 225 bis 274, 1901</ref> Für die Plejaden sind zahllose Synonyme im Gebrauch:<ref name="Grimm" /><ref name="HWBdA">Siehe auch: ''Handwörterbuch des deutschen Aberglaubens'', Band 9, ''Sternbilder II'', ''3. Plejaden'', Göschen'sche Verlagshandlung, 1941</ref><ref name="PleiadeAssociates" /><ref>Siehe auch: [[w:en:Pleiades in folklore and literature|Pleiades in folklore and literature]] in der englischsprachigen Wikipedia</ref> :'''Regensterne''', '''Schiffersterne''', '''Buschelsterni''', '''Staubkörner''', '''Sieb''', '''Glucke''', '''Henne''', '''Tauben''', '''Weintraube''', '''Traube''', '''Frühlingsjungfern''', '''Sieben Schwestern''', '''Töchter des Atlas''' (auch '''Atlantiden''', '''Atlantiaden'''), ... [[Datei:MUL.MUL.png|mini|hochkant=2|Assyrische Keilschrift mit zwei gleichen Schriftzeichen der sumerischen Bedeutung MUL.MUL (wörtlich übersetzt: Stern.Stern = Sterne beziehungsweise Sternhaufen) für die Plejaden.]] [[Datei:Subaru.AbeSeimei.png|mini|hochkant=2|Zeichen für die Plejaden nach dem japanischen Kosmologen Abe no Seimei (*&nbsp;921; †&nbsp;1005).<ref>Teru Karasawa: [https://dl.ndl.go.jp/info:ndljp/pid/759925/58 Abe no Seimei], Doppelseite 58, Shinseikan, Tokio, 1912</ref>]] In den meisten Sprachen hatten und haben sie einen Eigennamen: :althochdeutsch '''thaz sibunstirri''' (das Siebenstirn), polnisch '''baby''' (alte Weiber), russisch '''baba''' (altes Weib), japanisch '''subaru''' (Versammlung), türkisch '''ülker''', aztekisch '''tianquiztli''' (Marktplatz), sumerisch '''MUL.MUL''' (in Keilschrift: [[Datei:Assyrian_cuneiform_U1202F_MesZL_247.svg|40px]] [[Datei:Assyrian_cuneiform_U1202F_MesZL_247.svg|40px]], Plural, wörtlich "Stern-Stern" = Sterne), akkadisch '''zappu''' (Haufen / Borste / Kamm) sowie '''sebettu''' (die Sieben), avestisch '''hapta srva''' (wörtlich "sieben Hornstücke"), aramäisch und hebräisch '''Kimah''' (Häuflein), arabisch '''al-thurayya''' (kleine Reichliche, die vielen Kleinen oder Kronleuchter)<ref>Emilie Savage-Smith: ''A Descriptive Catalogue of Oriental Manuscripts at St John's College'', Seite 132, St. John's College, University of Oxford, Oxford University Press, 2005, ISBN 9780199201952</ref>, lateinisch '''vergiliae''' (Geflecht), griechisch '''heptasteros''' (Siebenstern), indisch '''krittika''' (sechs Nymphen, die ihren Sohn, den hinduistischen Gott Karttikeya, aufzogen), chinesisch '''mǎo''' (昴&nbsp;= haariger Kopf des Sternbilds "Weißer Tiger"), australisch '''mormodellick'''<ref name="ErnstVonBunsen">Ernst von Bunsen: ''Die Plejaden und der Thierkreis oder: Das Geheimnis der Symbole'', Verlag von Mitscher und Röstell, Berlin, 1879</ref>, maorisch '''matariki''', polynesisch '''matarii''' (Gesellschaftsinseln)<ref name="ErnstVonBunsen" />, hawaiisch '''makaliʻi''' (auch im Zusammenhang mit einer "Schöpfkelle" oder als "Augen des Chefs" beziehungsweise "Gottes Augen")<ref>Sergei Rjabchikov: [https://arxiv.org/ftp/arxiv/papers/1610/1610.08966.pdf The Ancient Astronomy of Easter Island: Aldebaran and the Pleiades], 2016</ref>, inuitisch '''sakiattiak''' (Brustbein), Ojibwe '''bugonagiizhig''' (Loch im Himmel) sowie '''madoo'asinik''' (schwitzende Steine), Mittleres Sioux '''wiçinyanna sakowin''' / '''wiçincala sakowin''' (sieben Mädchen), aragonesisch '''as crabetas''', walisisch '''twr tewdws''', samisch '''rougot''' (Hundemeute), finnisch '''seulaset''' (Siebchen oder Siebengestirn). Es sei angemerkt, dass das finnische Wort "seula" in "seulaset" für das deutsche Wort "Sieb" steht (althochdeutsch "sib"), das Suffix "-set" steht für die Verkleinerungsform "-chen", "seulaset" heißt wörtlich übersetzt also "Siebchen". Die Assoziation eines kleinen Siebes mit einem offenen Sternhaufen ist augenfällig, wobei die Anzahl der Löcher respektive der Sterne natürlich keineswegs genau sieben betragen muss. Bei den babylonischen Chaldäern hießen die Plejaden auch '''Tamsil''' (zu Deutsch "Herde" beziehungsweise "Versammelte" oder "Genossen").<ref name="Ueberlieferung">Ernst von Bunsen: ''Die Überlieferung. Ihre Entstehung und Entwicklung'', sechstes Kapitel "Früheste astronomische Beobachtungen", Friedrich Arnold Brockhaus, Leipzig, 1889</ref> === Bibelstellen === Im '''Alten Testament''' wird der Sternhaufen der Plejaden drei Mal erwähnt, allerdings weisen die verschiedenen Übersetzungen keine einheitlichen Bezeichnungen oder Begriffe auf.<ref name="Job">Siehe hierzu auch: Antoine-Yves Goguet, Alexandre-Conrad Fugère: [https://www.google.de/books/edition/The_Origin_of_Laws_Arts_and_Sciences/sVl5nkgat3cC?hl=de&gbpv=1&pg=PA395 ''Upon the Constellations which are spoke of in the Book of Job'', Dissertation III.], Band I.: ''The Origin of Laws, Arts, and Sciences And Their Progress Among the Most Ancient of Nations'' – ''From the Deluge to the Death of Jacob'' ("Von der Sintflut bis zum Tod von Jakob"), Seiten 395 bis 402, Verlag George Robinson & Alexander Donaldson, Edinburgh, 1775</ref><ref>Emil G. Hirsch: [https://www.jewishencyclopedia.com/articles/4626-constellations Constellations], Jewish Encyclopedia, 2002-2021</ref> Das Sternbild Orion wird in der Anfangszeit des ersten vorchristlichen Jahrtausends neben den beiden offenen Sternhaufen der Plejaden und der Hyaden sowie dem Sternbild der Bärin beziehungsweise dem Himmelswagen (Großer Wagen in der Großen Bärin (Ursa Major)) auch im griechischen Raum als eines der sehr wenigen Sternbilder in den Epen ''Ilias''<ref>[https://www.projekt-gutenberg.org/homer/ilias/ilias183.html Homer - Ilias], projekt-gutenberg.org</ref> und ''Odyssee''<ref>[http://www.zeno.org/Literatur/M/Homer/Epen/Odyssee/11.+Gesang Homer - Epen - Gesang XI.], zeno.org</ref> von Homer genannt. Diese sehr auffälligen und gut zu beobachtenden Asterismen könnten in den alten Texten als Prototypen von Sternbildern beziehungsweise der untereinander in festen Abständen befindlichen Fixsterne gedeutet werden. Das 9.&nbsp;Kapitel „Gottes Macht und die Ohnmacht des Menschen“ des Buches '''Hiob''' erwähnt die vier auffälligsten Sternkonstellationen im 9.&nbsp;Vers:<ref>[https://www.bibleserver.com/EU/Hiob9 Hiob 9], Einheitsübersetzung, 2016</ref> <blockquote> Einheitsübersetzung (2016):<br/> 7 Er spricht zur Sonne, sodass sie nicht strahlt, er versiegelt die Sterne.<br/> 8 Er spannt allein den Himmel aus und schreitet einher auf den Höhen des Meeres.<br/> 9 Er macht das Sternbild des '''Bären''', den '''Orion''', das '''Siebengestirn''', die '''Kammern des Südens'''.<br/><br/> Vulgata:<br/> 9 Qui facit '''Arcturum''' et '''Oriona''' et '''Hyadas''' et '''interiora austri'''.<br/><br/> Septuaginta:<br/> 9 ὁ ποιῶν '''Πλειάδα''' ("Pleiada") καὶ '''Εσπερον''' ("Esperon") καὶ '''Αρκτοῦρον''' ("Arktouron") καὶ '''ταμιεῖα νότου''' ("tamieia notou") </blockquote> Das Name '''Αρκτοῦρον''' ("Arktouron") leitet sich aus den griechischen Wörtern ἄρκτος ("arktos" für "Bär") and οὖρος ("ouros" für "Hütter") her und bedeutet demzufolge "Bärenhüter". Der Bärenhüter (Bootes) ist ein auffälliges und großes Sternbild in der Verlängerung der Deichsel des Großen Wagens im Großen Bären (Ursa Major) mit dem hellen Hauptstern Arktur (Arcturus, α Bootis), bei dem es sich ebenso wie bei Aldebaran (α Tauri) im Stier (Taurus) und bei Antares (α Scorpii) im Skorpion um einen Roten Riesen handelt. Diese beiden ebenfalls sehr hellen und auffälligen Sterne markierten in der Mitte des dritten vorchristlichen Jahrtausends den Frühlingspunkt im Stier und den Herbstpunkt im Skorpion ([[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Plejaden#Als_Bezugspunkt|siehe oben]]). Der '''erste Asterismus''' bei Hiobs Aufzählung lautet im Hebräischen '''Aisch''' ("איש"), was ebenso wie die entsprechende arabischsprachige Wurzel "Aouas" mit "einen Kreis machen" auch als "zusammenrotten" oder "versammeln" gedeutet werden kann.<ref name="Job" /> Diese Interpretation steht im Einklang mit den entsprechenden Bedeutungen im Japanischen, im Akkadischen oder im babylonischen Chaldäisch für das Siebengestirn ([[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Plejaden#Überlieferungen|siehe oben]]). Die deutschsprachige Buber-Rosenzweig-Übersetzung von 1929 interpretiert diesen Asterismus als "Leun", also als Löwen. Das altgriechische Wort "Esperon" beim '''zweiten Asterismus''' der Aufzählung bedeutet "Abendstern", und diese Bezeichnung taucht als "Vesperum" auch in einigen Versionen der Vulgata auf.<ref name="Job" /> Im Hebräischen steht '''Kimah''' ("כימה"), was häufig mit "Sternhaufen" übersetzt wird<ref name="ErnstVonBunsen" /><ref>[https://biblehub.com/hebrew/3598.htm Bible > Strong's > Hebrew > 3598. Kimah], biblehub.com</ref>, aber auch vom Wort "Kamah" ("כמה") abstammen könnte, welches wiederum so viel wie "begehren" oder "jubeln" bedeutet. Das arabische Pendant "Kaouam" beziehungsweise "Kam" charakterisiert den Frühling.<ref name="Job" /> Der Frühlingspunkt lag vor 4600 Jahren beim Siebengestirn, was auch noch bis ins 7.&nbsp;Jahrhundert vor Christus auf den assyrischen MUL.APIN-Tafeln überliefert ist, die bis ungefähr 300&nbsp;vor Christus in Babylonien immer wieder kopiert wurden. Das Wort "Kam" kann auch zur Veranschaulichung einer "Schar" oder einer "Vielfalt" dienen". Letzteres kann leicht mit der wörtlichen Bedeutung des altgriechischen Begriffs "Pleiada" in Bezug gesetzt werden.<ref name="Job" /> An '''dritter Stelle''' folgt im Hebräischen die Konstellation '''Kesil''' ("כזיל"). Die Wurzel dieses Wortes ist "Kasal" ("כזל"), was so viel wie "wechselhaft" bedeutet, wohingegen im Arabischen "starr" oder "kalt" die passenden Bedeutungen sind. Insofern kann in "Kesil" der Gegenpol zu "Kimah" am Sternenhimmel beziehungsweise bei den Jahreszeiten sowie als der Gegensatz zwischen Bewegung und Starrheit angesehen werden.<ref name="Job" /> Einige Interpretatoren gehen darauf basierend davon aus, dass es sich bei den Gegenpolen um zwei gegenüberliegende Sternbilder oder auch nur Sterne respektive Sternbilder<ref>[https://biblehub.com/hebrew/3685.htm Bible > Strong's > Hebrew > 3685. Kesil], biblehub.com</ref> handeln könnte, wie der Rote Riese Aldebaran (α Tauri) im Wintersternbild Stier (Taurus) und der Rote Überriese Antares (α Scorpii) im Sommersternbild Skorpion (Scorpio).<ref name="Job" /> Die Sternbilder Orion und Stier (Taurus) mit den Plejaden und den Hyaden werden auch Wintersternbilder genannt, weil sie im Winter um Mitternacht auf dem südlichen Meridian kulminieren, wo sie vollständig und besonders gut zu sehen sind. Umgekehrt ist es mit den zu den Wintersternbildern auf dem Lebewesenkreis diametralen Sommersternbildern Skorpion (Scorpio) und Adler (Aquila) in der Sommermilchstraße, die im Sommer um Mitternacht auf dem südlichen Meridian kulminieren. Im Hebräischen steht an der '''vierten Stelle''' der Plural '''Mazzaroth''' ("מזרות"), was einfach Sternbilder<ref name="Job" /><ref>Siehe auch: Yosef Qafih (Herausgeber): ''Job, with a Translation and Commentary of Rabbi Saadia ben Yosef Fayyumi'', Committee for the publication of Rabbi Saadia Gaon's books, in affiliation with the American Academy of Jewish Studies. Seite 189, Jerusalem, 1973</ref> oder etwas spezieller Zodiak (Lebewesenkreis) bedeuten könnte. Letzteres gilt insbesondere, wenn das ursprüngliche Wort "Nazar" ("נזר") zugrunde gelegt wird, welches "umzingeln" beziehungsweise "umkreisen" bedeutet. In diesem Sinne könnten mit den (geheimen) Kammern des Südens auch alle Sterne gemeint sein, die zirkumpolar um den südlichen Himmelspol kreisen, somit nie auf der nördlichen Hemisphäre sichtbar werden und demzufolge verborgen sind.<ref name="Job" /> Die Kleinschreibung in den alten Sprachen suggeriert, dass es sich bei den "Kammern des Südens" nicht um den Namen für einen Asterismus handeln könnte. Im weiteren Sinne könnten auch südlich gelegene [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Konjunktionen#Mondhäuser|'''Mondhäuser''']] (oder "Mondstationen" respektive "Mondkammern") gemeint sein, in denen sich der Mond in der Nähe des südlichen Meridians, wegen der dort auftretenden oberen Kulmination gut erkennbar, jeweils einen Tag lang aufhält.<ref name="Ueberlieferung"/> Diese Annahme wird unterstützt durch den ähnlich klingenden assyrischen Begriff "manzaltu" für "Station". Andere Autoren gehen davon aus, dass mit den Kammern des Südens die Milchstraße gemeint ist.<ref>René Nyffenegger: [https://renenyffenegger.ch/Biblisches/Kommentare/hi_9.html Hiob 9], Kommentare zur Bibel</ref> Da der hebräische Begriff "Mazzaroth" im 2.&nbsp;Buch der Könige, 23.&nbsp;Kapitel, Vers 5 (in der Septuaginta steht entsprechend "μαζουρωθ" ("mazouroth") in vielen Übersetzungen mit den zwölf Sternbildern des Zodiaks gleichgesetzt wird, wäre auch die Identifikation der "Kammern des Südens" mit den Sternbildern des Lebewesenkreises denkbar, die bei der Kulmination auf dem südlichen Meridian besonders gut und in hinreichend südlichen Breiten stets vollständig zu sehen sind. Im 31.&nbsp;Vers des 38.&nbsp;Kapitels des Buches '''Hiob''' heißt es dann:<ref>[https://www.bibleserver.com/EU/Hiob38 Hiob 38], Einheitsübersetzung, 2016</ref> <blockquote> Buber-Rosenzweig-Übersetzung (1929):<br/> 31 Knüpfst du das Gewinde der '''Glucke''' oder lösest du dem '''Orion''' die Bande?<br/> 32 führst zu seiner Frist du hervor das '''Zerstiebergestirn''', und die Löwin samt ihren Söhnen, hütest du sie?<br/> 33 Kennst du die '''Umschränkungen des Himmels'''? setzest du auf die Erde seine Urkunde nieder?<br/><br/> Einheitsübersetzung (2016):<br/> 31 Knüpfst du die Bande des '''Siebengestirns''' oder löst du des '''Orions''' Fesseln?<br/> 32 Führst du heraus '''Sterne des Tierkreises''' zu seiner Zeit, lenkst du die Löwin samt ihren Jungen?<br/> 33 Kennst du die '''Satzungen des Himmels''', setzt du auf der Erde seine Herrschaft durch?<br/><br/> Vulgata:<br/> 31 Numquid coniungere valebis nexus stellarum '''Pleiadum''' aut funiculum '''Arcturi''' poteris solvere&nbsp;?<br/> 31 ''(alternativ)'' Numquid coniungere valebis micantes stellas '''Pliadis''' aut gyrum '''Arcturi''' poteris dissipare<br/> 32 Numquid producis '''luciferum''' in tempore suo et vesperum super filios terrae consurgere facis<br/> 33 Numquid nosti '''ordinem caeli''' et pones rationem eius in terra<br/><br/> Septuaginta:<br/> 31 συνῆκας δὲ δεσμὸν '''Πλειάδος''' ("Pleiados") καὶ φραγμὸν '''Ωρίωνος''' ("Orionos") ἤνοιξας<br/> 32 ἦ διανοίξεις '''μαζουρωθ''' ("mazouroth") ἐν καιρῷ αὐτοῦ καὶ '''Eσπερον''' ("Esperon") ἐπὶ κόμης αὐτοῦ ἄξεις αὐτά;<br/> 33 ἐπίστασαι δὲ '''τροπὰς οὐρανοῦ''' ("tropas ouranou") ἢ τὰ ὑπ '''οὐρανὸν''' ("ouranon") ὁμοθυμαδὸν γινόμενα; </blockquote> Auch hier sind die Zusammenhänge und Übersetzungen sehr rätselhaft. Die "Sterne des Tierkreises" (des Zodiaks) im Vers 32 heißen im Hebräischen ("מזרות") und im Griechischen ("μαζουρωθ") '''mazouroth''' und wurden in der lateinischsprachigen Vulgata mit "luciferum" übersetzt, also mit "Morgenstern". Von einer Löwin ist weder im Lateinischen noch im Griechischen oder im Hebräischen die Rede. Auch im Vers 31 steht für das Siebengestirn und den Orion im Hebräischen "Kimah" ("כימה") und "Kesil" ("כזיל")<ref>[https://biblehub.com/text/job/38-31.htm Bible > Hebrew > Job 38:31], biblehub.com</ref>, was in Bezug auf die einstige Lage des Frühlingspunktes bei den Plejaden und dessen Wanderung durch die Präzession der Erdachse auch zur folgenden astronomischen Interpretation führen könnte: <blockquote> „Knüpfst du die Bande des Frühlings(-punktes), oder löst du die Fesseln der Sterne&nbsp;?“ </blockquote> Im Buch des Propheten '''Amos''' im Kapitel&nbsp;5, Vers&nbsp;8 heißt es zu den beiden benachbarten Konstellationen: <blockquote> Einheitsübersetzung (2016):<br/> Er hat das '''Siebengestirn''' und den '''Orion''' erschaffen;<br/> er verwandelt die Finsternis in den hellen Morgen, er verdunkelt den Tag zur Nacht,<br/> er ruft das Wasser des Meeres und gießt es aus über die Erde – HERR ist sein Name.<br/> <br/> Nova Vulgata:<br/> Qui facit stellas '''Pliadis''' et '''Orionem'''<br/> <br/> Vulgata:<br/> facientem '''Arcturum''' et '''Orionem'''<br/> <br/> Septuaginta:<br/> ποιῶν '''πάντα''' καὶ '''μετασκευάζων''' </blockquote> Die griechischen Wörter sind unspezifisch für konkrete Sternkonstellationen. Das Wort "panta" steht für "alles" (oder "immer"), und das Wort "metaskeuazon" könnte mit den beiden Bestandteilen "meta" ("nach", "inmitten") und "skeuazon" ("einrichten", "fertig machen", "vorbereiten", "sammeln") folgendermaßen interpretiert werden: <blockquote> „Er hat alles erschaffen und es gestaltet; er verwandelt die Finsternis in den hellen Morgen, er verdunkelt den Tag zur Nacht.“ </blockquote> Auch hier werden im Hebräischen die Wörter "Kimah" ("כימה") und "Kesil" ("כזיל") verwendet (siehe oben).<ref>[https://www.bibelwissenschaft.de/online-bibeln/biblia-hebraica-stuttgartensia-bhs/lesen-im-bibeltext/bibel/text/lesen/stelle/30/50001/59999/ch/006b2dd3e5e3f07583001cb3beb96798/ Amos 5]</ref> Der Lauf der Sonne bestimmt die Jahreszeiten, und das Jahr begann im Altertum in der Regel mit dem Frühlingsbeginn, wenn die Sonne zur Tag-und-Nacht-Gleiche im Frühlingspunkt steht. Die Plejaden lagen um 2600&nbsp;vor Christus beim Frühlingspunkt und standen somit in der uralten Tradition für den Jahres- beziehungsweise Frühlingsbeginn (→ siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Plejaden#Schaltregeln|'''Plejaden-Schaltregeln''']]). Ein halbes Jahr später steht die Sonne zur Tag-und-Nacht-Gleiche im Herbstpunkt auf der gegenüberliegenden Seite der Ekliptik. Alle anderen Wandelgestirne ziehen mit unterschiedlichen Umlaufzeiten ebenfalls entlang der Ekliptiklinie regelmäßig durch diese beiden Punkte. Das '''[[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/ Astronomische Bezugssysteme#Das_Goldene_Tor_der_Ekliptik|Goldene Tor der Ekliptik]]''' wird durch die beiden offenen Sternhaufen der Plejaden und der Hyaden gebildet und beherbergte deswegen vor 4600&nbsp;Jahren den Frühlingspunkt. Dieses traditionelle Wissen wurde in Babylonien mit den MUL.APIN-Tafeln bis ins siebente vorchristliche Jahrhundert aus den älteren Zeiten bewahrt, so dass der Prophet Amos im Königtum Juda durch die Einflüsse des Neuassyrischen Großreiches darüber informiert gewesen sein kann. Zum einen stehen die ewig fest an ihren Positionen stehenden Sterne (lateinisch ''stellae fixae'') und Sternbilder den sich ewig entlang der scheinbaren Sonnenbahn (Ekliptik) bewegenden Wandelgestirne (lateinisch ''stellae errantes'', im weiteren Sinne auch die Kometen (lateinisch ''stellae crinitae'' = "langhaarige Sterne")) gegenüber. Zum anderen liegen der Frühlings- und der Herbstpunkt (Äquinoktien) sowie die Winter- und die Sommersonnenwende (Solstitien) auf gegenüberliegenden Seiten der Ekliptik. Jeder dieser vier auf der Ekliptiklinie ausgezeichneten Punkte kreist bedingt durch die Rotation der Erde an jedem Tag einmal um einen irdischen Beobachter, wobei während der Nacht immer nur der der jeweiligen Jahreszeit entsprechende Ausschnitt des Sternenhimmels zu sehen ist. Im Laufe von Jahrhunderten verschieben sich die Äquinoktien und die Solstitien gegenüber dem Fixsternhimmel ersichtlich. Im Sinne dieser räumlichen und zeitlichen Zusammenhänge, Gegensätze und Veränderungen könnte also eine einfache, aber durchaus naheliegende astronomische Interpretation dieses Amos-Textes in Betracht kommen: <blockquote> „Er hat den ewigen Himmelslauf erschaffen; er verwandelt die Finsternis in den hellen Morgen, er verdunkelt den Tag zur Nacht.“ </blockquote> === Bezug zur Vierzig === Die Plejaden hatten in vielen Kulturen also eine besondere Bedeutung und tauchen häufig in bildlichen Darstellungen auf. Sie sind ein Kalendergestirn, nach dessen Auf- und Untergängen schon im Altertum landwirtschaftliche und seefahrerische Tätigkeiten ausgerichtet wurden, wie es zum Beispiel schon bei den griechischen Dichtern {{w|Hesiod}} um 700 vor Christus<ref>Hesiodos: [https://www.gottwein.de/Grie/hes/ergde.php Werke und Tage (ΕΡΓΑ ΚΑΙ ΗΜΕΡΑΙ)], Egon und Gisela Gottwein, 13. Juni 2019</ref><ref>Hesiod: [https://www.projekt-gutenberg.org/hesiod/hauslehr/hausleh2.html Hauslehren II. (’Έργα καὶ ‛ημέραι)], Projekt Gutenberg.de, übersetzt von Johann Heinrich Voß</ref> oder {{w|Aratos von Soloi}} (*&nbsp;zirka 310 vor Christus; †&nbsp;245 vor Christus) belegt ist. Hesiod erwähnt in seinem Text auch, dass die Plejaden im Frühjahr für vierzig Tage und Nächte nicht zu sehen sind, da sie vom Sonnenlicht überstrahlt werden. Der Name ''Plejaden'' geht auf die sieben Töchter des Titanen ''Atlas'' und seiner Gattin, der Okeanide ''Pleione'', aus der griechischen Mythologie zurück. Sie heißen: ''Alkyone'', ''Halcyone'', ''Asterope'' (oder ''Sterope''), ''Kelaino'', ''Maia'', ''Merope'' und ''Taygete''. Der Begriff '''Quarantäne''' (vom Französischen „quarantaine (de jours)“ = „vierzig Tage“) soll mit den Plejaden zusammenhängen, da diese in den subtropischen Breiten (heute) vom 1.&nbsp;Mai bis zum 9.&nbsp;Juni, also vierzig Tage lang, von der Sonne überstrahlt werden und dann selbst der hellste Stern dieser Konstellation, Alkione (η&nbsp;Tauri), mit bloßem Auge erst kurz nach Sonnenuntergang nicht mehr und dann kurz vor Sonnenaufgang noch nicht wieder gesehen werden kann. Nach der Unsichtbarkeit der Plejaden begann im alten Ägypten vierzig Tage lang das '''Nilwasser''' zu steigen und ebenso lange wieder zu fallen.<ref>Christian Schulz: ''Handbuch der Physik: für diejenigen welche Freunde der Natur sind, ohne jedoch Gelehrte zu seyn'', Band 2, Kapitel 11, Seite 254, Hilscher, Leipzig, 1791</ref> '''Noah''' öffnete nach vierzig Tagen das Fenster seiner '''Arche'''<ref>[https://www.bibleserver.com/EU/1.Mose8%2C6 Genesis, Kapitel 8, Vers 6], bibleserver.com, Einheitsübersetzung (2016)</ref>, und Moses verbrachte vierzig Tage auf dem Gottesberg Sinai.<ref>[https://www.bibleserver.com/EU/2.Mose24%2C18 Exodus, Kapitel 24, Vers 18], bibleserver.com, Einheitsübersetzung (2016)</ref> Es ist vor diesem Hintergrund nicht verwunderlich, dass im Neuen Testament Jesus dann auch vierzig Tage in der Wüste fastet<ref>[https://www.bibleserver.com/EU/Matth%C3%A4us4 Evangelium nach Matthäus, Kapitel 4], bibleserver.com, Einheitsübersetzung (2016)</ref><ref>[https://www.bibleserver.com/EU/Lukas4 Evangelium nach Lukas, Kapitel 4], bibleserver.com, Einheitsübersetzung (2016)</ref><ref>[https://www.bibleserver.com/EU/Markus1%2C12 Evangelium nach Markus, Kapitel 1, Vers 12 und 13], bibleserver.com, Einheitsübersetzung (2016)</ref>, weswegen es in der österlichen Bußzeit heute ebenfalls vierzig Fastentage gibt. Siehe auch: [[Quadriviale_Kuriositäten/_Zahlen#Zur_Vierzig| Quadriviale Kuriositäten / Zahlen / Zur Vierzig]] === Sagenwelt === [[Datei:Devils Tower CROP.jpg|mini|rechts|hochkant=2|Der Devils Tower im Nordosten des US-amerikanischen Bundesstaates Wyoming.]] Das Siebengestirn hat in sehr vielen Kulturen eine mythische Bedeutung. Als ein Beispiel unter vielen sei hier die folgende Erzählung der Kiowa im nordamerikanischen Wyoming über die Entstehung eines Berges in der Nähe ihres neu gegründeten Dorfes wiedergegeben. Bei den '''Kiowa-Indianern''' geht die Sage, dass sieben Mädchen sich vor mehreren Bären auf einen Felsen flüchteten und ihn anflehten sie zu retten. Daraufhin sei dieser heute als '''Devils Tower''' bekannte Vulkankegelstumpf immer weiter in den Himmel gewachsen und brachte die Mädchen schließlich als die Plejaden an das Firmament. Die von den Bärenkrallen an den Flanken des Berges verursachten vertikalen Schrammen seien immer noch zu sehen:<ref>[https://www.nps.gov/deto/learn/historyculture/first-stories.htm First Stories - Devils Tower National Monument (U.S. National Park Service)], Devils Tower National Monument Visitor Center, 17. März 2019</ref> <blockquote> Eines Tages spielten sieben kleine Mädchen in einiger Entfernung zum Dorf. Sie wurden von mehreren Bären entdeckt, und die Mädchen eilten zum Dorf. Die Bären jedoch erreichten die Mädchen weit vor dem Dorf. In ihrer Not kletterten die Mädchen auf einen kleinen Felsbrocken. Sie flehten den Stein an: „Fels, habe Mitleid mit uns, Fels rette uns“. Der Fels erhörte die Mädchen und fing an in die Höhe zu wachsen. Die Bären sprangen den Felsen in ihrer Wut an, brachen riesige Felsbrocken aus ihm heraus und kratzten mit ihren Krallen tiefe Rillen und Spalten in den Felsen, jedoch konnten sie die Mädchen nicht erreichen. Der Fels wuchs und wuchs bis in den Himmel hinein. Die Mädchen sind noch immer im Himmel, als sieben kleine Sterne am Firmament: die Plejaden. </blockquote> Die '''Inuit''' erzählen sich die Legende, dass ein großer Bär die Menschheit bedrohte und von Hunden an den Himmel verjagt wurde. Die Hundemeute würde als die Plejaden diesen Bären heute weiterhin verfolgen.<ref name="PleiadeAssociates">[http://www.pleiade.org/pleiades_02.html The Pleiades in mythology], Pleiade Associates, Bristol, United Kingdom</ref> Die australischen Ureinwohner der '''Loritja''' erzählen sich, dass sieben Mädchen während der Unsichtbarkeit der Plejaden auf die Erde kommen und einen Feuertanz aufführen.<ref>Carl Strehlow: ''Mythen, Sagen und Märchen des Loritja-Stammes'', Baer & Company, 1907</ref> Im Zusammenhang mit der Tatsache, dass der '''Kuckuck''' im Frühsommer aufhört zu singen und dass die Plejaden in den Breitengraden der klimatisch gemäßigten Zonen dann deutlich länger nicht zu sehen sind, gibt es eine deutsche Sage über einen hartherzigen Bäcker, der bis zur Sommersonnenwende 72 Tage lang vergeblich nach seiner Frau und seinen Töchtern ruft. In dieser Sage es heißt: :''Vom Ursprung der Plejaden wird erzählt: Christus ging an einem Bäckerladen vorüber, wo frisches Brot duftete, und sandte seine Jünger hin, ein Brot zu erbitten. Der Bäcker schlug es ab, doch von Ferne stand die Bäckersfrau mit ihren sechs Töchtern und gab das Brot heimlich. Dafür sind sie als Siebengestirn an den Himmel versetzt, der Bäcker aber ist zum Kuckuck geworden und solange er Frühjahrs ruft, von Tiburtii (''Anmerkung: Namenstag {{w|Tiburtii von Rom}} ist der 14.&nbsp;April'') bis Johannis (''Anmerkung: Namenstag {{w|Johannes der Täufer|Johannes' des Täufers}} ist der 24.&nbsp;Juni (Johannistag)''), ist das Siebengestirn am Himmel [nicht] sichtbar.''<ref name="Grimm">Jacob Grimm: [https://www.projekt-gutenberg.org/grimm/demyth/chap022.html Kapitel XXII - Himmel und Gestirne], Abschnitt ''Gestirne / Plejaden'', in: ''Deutsche Mythologie'', zweite Ausgabe von 1844</ref> In norddeutschen, ostpreußischen und böhmischen Sagen gibt es Varianten dieser Geschichte, bei denen der Kuckuck die geflüchteten Familienangehörigen nicht zurückrufen kann beziehungsweise deren Rache fürchtet.<ref name="HWBdA" /><ref>Oskar Dähnhardt: [http://www.zeno.org/nid/20007806434 3. Entstehung des Kuckucks: 1 Aus Ostpreußen / 2 Aus Mecklenburg / 3 Aus Pommern], Natursagen. Eine Sammlung naturdeutender Sagen, Märchen, Fabeln und Legenden]], 4 Bände, Leipzig/Berlin, 1907 bis 1912, Seiten 426 bis 428</ref> Eine Mecklenburgische Volksüberlieferung lautet: :''Viertig Dag un viertig Nacht darf de Kukuk sik man sehn laten, denn is dat Soebenstiern hier wech; wenn dat wedderkümmt, denn mööt de Kukuk wider.'' ("mööt" = "muss (weichen)")<ref>Richard Wossidlo: ''Mecklenburgische Volksüberlieferungen'', 2 ''Die Tiere im Munde des Volkes'', Verlag Hinstorff, Seite 411, 1899</ref> Bei zwei dänischen Varianten geht es um eine Frau mit sieben unehelichen Kindern und um ein zerstrittenes Ehepaar.<ref>Oskar Dähnhardt: [http://www.zeno.org/nid/20007806434 3. Entstehung des Kuckucks: 4 a) und 4 b) Aus Dänemark], Natursagen. Eine Sammlung naturdeutender Sagen, Märchen, Fabeln und Legenden]], 4 Bände, Leipzig/Berlin, 11907 bis 1912, Seiten 426 bis 428</ref><ref>Evald Tang Kristensen: ''Jayske Folkeminder IV'', 335, Nummer 428</ref> === Zusammenhang mit dem Stier === [[Datei:Stiersymbol.P1079912.png|mini|rechts|hochkant=1.5|Asterismus des Himmelsstieres mit den Bezeichnungen der hellsten Sterne. Die Plejaden liegen auf dem Rücken des Himmelsstieres.]] Der offene Sternhaufen der Plejaden hat sieben Sterne, die eine scheinbare Helligkeit von dritter bis fünfter Größenklasse haben und somit gut mit bloßem Auge zu erkennen sind. Die Plejaden liegen auf dem Rücken des [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Der_Himmelsstier|'''Himmelsstieres''']] und könnten aus diesen Gründen als Urquell der sieben Wandelgestirne angesehen werden. Althochdeutsch wird der Sternhaufen mit „sibunstern“, „sibunstirni“, oder „sibunstirri“ bezeichnet.<ref>Gerhard Köbler: [http://www.koeblergerhard.de/ahd/ahd_s.html althochdeutsch s], in: ''Althochdeutsches Wörterbuch'', 6. Auflage, 2014</ref> Die Übersetzung ins Lateinische ist nicht eindeutig (siehe oben), da hier sowohl "Vergiliae", „Pleiades“ und „Hyades“ als auch „septemtriones“<ref>Eduard Adolf Jacobi: [https://www.google.de/books/edition/Handw%C3%B6rterbuch_der_griechischen_und_r%C3%B6/uRMVAAAAYAAJ septemtriones], Seite 830, in: ''Handwörterbuch der griechischen und römischen Mythologie'', Band 2, Sinner'sche Hofbuchhandlung, Koburg und Leipzig, 1835</ref> („sieben Dreschochsen“) anzutreffen sind. Insbesondere bei den Wörtern für '''„sieben“''' und für '''„Stier“''' sowie '''„Gestirn“''' oder '''„Stern“''' sind die Ähnlichkeiten in den uralten (proto-indoeuropäischen und altsemitischen) Sprachen so auffällig, dass sie gemeinsame Ursprungswörter ('''Etyma''') haben und somit '''Kognaten''' sein dürften. Beispiele sind:<ref>Hermann Güntert: [https://www.google.de/books/edition/Les_g%C3%A8tes/93Kpr95vO0YC?hl=de&gbpv=1&dq=aram%C3%A4isch%20tora%20stier&pg=RA1-PA56&printsec=frontcover&bsq=aram%C3%A4isch%20 Indogermanisch und Semitisch], Kapitel V. ''Sprachliche Beziehungen der Indogermanen zu anderen Völkergruppen'', in: ''Kultur und Sprache'' / ''Der Ursprung der Germanen'', Seite 56, Carl Winter, Heidelberg, 1934</ref> * "'''sieben"''': Proto-Semitisch "*šabʕum", Akkadisch "sebe", Proto-Indoeuropäisch "*septḿ̥", Vedisch "sapta", Avestisch "hapta", Hethitisch "sipta", Proto-Germanisch "*sebun", Althochdeutsch / Gotisch "sibun", Hebräisch "sajin" (Buchstabe) oder "scheva" (Wort), Etruskisch "semph", Maltesisch "sebgħa", Arabisch "sabʿa", Lateinisch "septem", Griechisch "επτά" ("(h)epta"), Ungarisch "het", Proto-Balto-slawisch "*septin", Proto-Indo-Iranisch "*saptá", Katalanisch "set", Spanisch "siete", Galicisch "sete", Lettisch "septiņi", Italienisch "sette", Französisch "sept", Englisch "seven", Wallisisch "saith", Litauisch "septynì", Bosnisch / Kroatisch "sedam", Rumänisch "șapte", Irisch "seacht", Swahili "saba", Haitianisch "sèt" * "'''Stier"''':<ref>Siehe auch: Heinrich Wagner: ''Indogermanisch-Vorderasiatisch-Mediterranes'', in: ''Zeitschrift für vergleichende Sprachforschung auf dem Gebiete der Indogermanischen Sprachen'', 75. Band, Seiten 58 bis 75, Vandenhoeck & Ruprecht, 1957</ref> Akkadisch und Assyrisch "šūru", Aramäisch "tōra", Avestisch "staora", Mittelpersisch "stor", Neupersisch "sutor", Ostossetisch "sturla", Pahlavi "stor", Hebräisch "šǒr", Gotisch "stiur", Althochdeutsch "stior", Ugaritisch "twr", Lateinisch "taurus", Griechisch "ταύρος" ("tauros"), Arabisch "ثور" ("thawr"), Italienisch, Katalanisch und Spanisch "toro", Galicisch "touro", Französisch "taureau", Schwedisch "tjur", Dänisch "tyr", Gallisch "tarvos", Irisch und Gälisch "tarbh", Wallisisch "tarw" ** "'''Horn"''': Akkadisch "carnu", Aramäisch "qeren", Lateinisch "cornu", Griechisch "κόρνο" ("korno"), Maltesisch "qrun", Arabisch "قرون" ("qurun"), Französisch "corne", Italienisch "corne", Rumänisch "corn", Haitianisch "kòn" * "'''Stern / Gestirn"''': Indogermanisch "*ster", Akkadisch "istar", Avestisch "star", Lateinisch "astrum" / "stella", Griechisch "άστρο" / "αστέρι" ("astro" / "asteri"), Althochdeutsch "stern(o)", Galicisch "estrela", Katalanisch und Spanisch "estrella", Englisch "star", Isländisch "stjarna", Italienisch "stella", Sardisch "istedda", Maltesisch "stilla", Dänisch und Norwegisch "stjerne", Schwedisch "stjärna", Rumänisch "stea", Baskisch "izar" Im Althochdeutschen wären die lateinischen „septemtriones“ die „sibunstiori“, was den althochdeutschen „sibunstirri“ wiederum sehr ähnlich kommt. Es wäre demzufolge denkbar, dass der Himmelsstier als [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Der_Trichter_der_Thuraya|'''„Geburtstrichter“ der Thuraya''']] mit seinem Siebengestirn als „Geburtshelfer“ für die sieben Wandelgestirne angesehen wurde und dass die göttliche Zahl „Sieben“ mit den göttlichen Begriffen „Gestirn“ und „Stier“ im Laufe der Zeiten mit variierender Kombination, Bedeutung und Verwendung assoziiert wurde. :'''Anmerkung:''' :Eine Variante des lateinischen Wortes „septemtriones“ ist die lateinische Bezeichnung „septentrio“ für die nördliche Himmelsrichtung. Die vier Haupthimmelsrichtungen sind geographisch im Horizontsystem definiert. In Italien wurde das Sternbild Großer Bär (Ursa Major) "septentrio" ("Siebenfigur") genannt.<ref>Otto Keller: [https://www.google.de/books/edition/Zur_lateinischen_Sprachgeschichte/8DUTAAAAQAAJ?hl=de&gbpv=1&dq=Septentrio&pg=PA102 Zur lateinischen Sprachgeschichte - Septentrio], Seite 102 bis 104, Verlag Teubner, 1893</ref> Der Asterismus Großer Wagen im Sternbild Großen Bär besteht aus sieben sehr deutlich zu erkennenden Sternen und befindet sich von der Erde aus gesehen immer zwischen Nordwesten und Nordosten. Der Große Wagen ist in nördlichen Breiten seit Jahrtausenden zirkumpolar, befindet sich also nie unterhalb des Horizonts und steht keineswegs immer dicht über dem nördlichen Horizont. Nur im Sommer erfolgt die untere Kulmination auf dem nördlichen Meridian um Mitternacht. :Es möge in diesem Zusammenhang zur Kenntnis genommen werden, dass sich die nördlichen Richtungen auch als Wohnstatt von sieben Gestirnen gesehen werden können. Hierfür kommen sowohl das Siebengestirn als auch die sieben Wandelgestirne in Frage, die im Norden nie zu sehen sind, weil sich die Ekliptiklinie dort stets unterhalb des Horizonts befindet. Das gleiche gilt für den Himmelsstier. Unabhängig von der genauen Bedeutung könnte also durchaus erwogen werden, dass sich der Begriff „septentrio“ von der Richtung ableitet, in welcher die „septemtriones“ (die „sieben Ochsen“) nie zu sehen sind, sich also in ihrem Ruheort verbergen. == Schaltregeln == [[Datei:MulApin-BritishMuseum.jpg|rechts|mini|Zweispaltige babylonische MUL.APIN-Tafel aus Ton mit Keilschrift im Britischen Museum in London. Die erste Tafel enthält astronomische Abhandlungen zu Himmelsabschnitten, Daten von Auf- und Untergängen wichtiger Sterne sowie 18&nbsp;Mondstationen inklusive der drei ersten Mondstationen: die '''Plejaden''', der '''Himmelsstier''' und '''Orion'''.]] [[Datei:MesoamerikanischerMond.CodexBorbonicus.png|mini|rechts|Mond-Sonne-Darstellung aus dem mesoamerikanischen '''Codex Borbonicus''' der Azteken vom Anfang des 16.&nbsp;Jahrhunderts. Die beiden sich zu einem vollen Kreis ergänzenden Halbkreise symbolisieren den Mond und die Sonne.<br/><br/> Im Halbkreis des Mondes befinden sich '''19&nbsp;Kreise'''. Er wird umkränzt von '''11&nbsp;kleineren Kreisen'''. Der Halbkreis der Sonne besteht aus '''7&nbsp;halbkreisförmigen Schalen''' (von innen nach außen: rot, orange, blau, rot, grün, weiß, orange), in denen sich '''7&nbsp;Strahlen''' befinden (3&nbsp;Dreiecke und 4&nbsp;Rechtecke), die nach außen zeigen und auf dem Rand des äußersten Kreises enden.<br/><br/> Folgende Beobachtungen sind hierbei interessant:<br/><br/> • Die '''11&nbsp;kleinen Kreise''' am Rand des Mondes korrespondieren mit den '''11&nbsp;Tagen''', die ein Sonnenjahr (365&nbsp;Tage) länger dauert als '''12&nbsp;synodische Monate''' (354&nbsp;Tage).<br/><br/> • Die '''7&nbsp;Strahlen''' an der Sonne und die '''7&nbsp;Halbschalen''' in der Sonne entsprechen den '''7&nbsp;Wandelgestirnen'''.<br/><br/> • '''7&nbsp;synodische Schaltmonate''' (das entspricht 207&nbsp;Tagen) sind erforderlich, um '''19&nbsp;Mondjahre''' mit je '''12&nbsp;synodischen Monaten''' (das entspricht 228&nbsp;synodischen Monaten (6733 Tage)) mit '''19&nbsp;Sonnenjahren''' zu synchronisieren (das entspricht 235&nbsp;synodischen Monaten (6940&nbsp;Tage)).]] Die Ermittlung von Regeln für die Synchronisation der unabhängigen Umläufe der Erde um die Sonne ('''Sonnenjahr''' mit vier Jahreszeiten) mit den Umläufen des Mondes um die Erde ('''synodischer Monat''' mit vier Mondphasen) oder mit der Rotation der Erde um ihre eigene Achse ('''Tag''' mit vier Tageszeiten) erfordert ein systematisches Beobachten des Geschehens am Himmel über sehr viele Jahre. Die Periodendauern dieser Umläufe stehen in keinem einfachen ganzzahligen Verhältnis zueinander. Die ältesten tradierten Schaltregeln, mit denen ein Ausgleich zwischen den unterschiedlichen Perioden hergestellt werden kann, stellen ein bemerkenswertes Zeugnis für die präzise astronomische Beobachtungsgabe, für die hohen kognitiven Fähigkeiten und für die sorgfältig gesammelten Erkenntnisse ihrer Erfinder dar. Die vierundzwanzig Stunden des Tages werden von 0 bis 23 durchgezählt, und die sieben Tage der Wochen (respektive Mondviertel) sowie die zwölf Monate des Jahres haben Eigennamen erhalten. In der folgenden Tabelle sind die drei oben genannten astronomischen Perioden mit ihren durch Beobachtung wahrnehmbaren Ausprägungen sowie den dazugehörigen Begriffen aufgeführt: {| class="wikitable" |+ Astronomische Perioden ! Zeitbegriff !! Tag !! Synodischer Monat || Sonnenjahr |- | '''Dauer''' || ≈ 24 Stunden || ≈ 29,5 Tage || ≈ 365,25 Tage |- | '''Rotierender Himmelskörper''' || Erde || Mond || Erde |- | '''Rotationszentrum''' || Erdachse || Erde || Sonne |- | '''Bezeichnung der jeweils<br/>vier Zeitabschnitte''' || Tageszeiten mit jeweils<br/>sechs Stunden || Mondphasen mit jeweils<br/>einer Siebentagewoche || Jahreszeiten mit jeweils<br/>drei Monaten |- | '''Erster Zeitpunkt''' || Sonnentiefststand<br/>(Mitternacht) || Neumond || Frühlingsbeginn |- | '''Erster Zeitabschnitt''' || Zweite Nachthälfte<br/>(0 bis 6 Uhr) || Erstes Mondviertel<br/>(zunehmender Mond) || Frühling |- | '''Zweiter Zeitpunkt''' || Sonnenaufgang<br/>(Morgen) || Zunehmender Halbmond || Sommeranfang |- | '''Zweiter Zeitabschnitt''' || Vormittag<br/>(6 bis 12 Uhr) || Zweites Mondviertel<br/>(zunehmender Mond) || Sommer |- | '''Dritter Zeitpunkt''' || Sonnenhöchststand<br/>(Mittag) || Vollmond || Herbstbeginn |- | '''Dritter Zeitabschnitt''' || Nachmittag<br/>(12 bis 18 Uhr) || Drittes Mondviertel<br/>(abnehmender Mond) || Herbst |- | '''Vierter Zeitpunkt''' || Sonnenuntergang<br/>(Abend) || Abnehmender Halbmond || Winteranfang |- | '''Vierter Zeitabschnitt''' || Erste Nachthälfte<br/>(18 bis 24 Uhr) || Viertes Mondviertel<br/>(abnehmender Mond) || Winter |} Seit 1978 ist bekannt, dass die mesopotamischen Keilschrifttexte des MUL.APIN, die seit dem siebenten Jahrhundert vor Christus nachgewiesen sind, die sogenannte '''Plejaden-Schaltregel''' beschreiben. Sie beziehen sich hierbei auf eine Zeit, die deutlich vor der Entstehung der noch erhaltenen und offenbar (mehrfach) kopierten Tontafeln liegt, nämlich auf das '''26.&nbsp;Jahrhundert vor Christus'''.<ref name="Papke" /> Dies ergibt sich aus den in den Texten explizit angegebenen astronomischen Daten zu den Sichtbarkeiten der Plejaden (sumerische Bezeichnung MUL.MUL = "Sterne") und den Monatsanfängen. Aufgrund der Präzession der Erdachse verschiebt sich zum einen der Frühlingspunkt entlang der Ekliptik immer weiter nach Westen (kleinere ekliptikale Längen). Zum anderen verändern sich auch die Aufgangs- und Untergangsazimute der Gestirne ein wenig. <gallery caption="Neubabylonischer Keilschrifttext VAT 04956" perrow=1 widths=960 heights=480> VAT04956.Neulicht.Saturn.P1068068.jpg|Auf der neubabylonischen Tontafel mit Keilschrifttext (VAT 04956, Vorderasiatisches Museum, Berlin) aus dem 37.&nbsp;Regierungsjahr des neubabylonischen Königs Nebukadnezar&nbsp;II. wird erwähnt, dass zu Frühlingsbeginn des Jahres 568 vor Christus das Neulicht des Mondes im Stierkopf zu sehen war und der Saturn vor der Schwalbe (Sternbild Fische) stand. Abendlicher.Sternenhimmel.Babylonien.25.3.-567.png|Simulierte Darstellung des entsprechenden Ausschnitts über dem westlichen Horizont (grüne Linie) am Abendhimmel. Die Wandelgestirne sind wie angegeben vergrößert dargestellt:<br/>Sin = Mond, mul.DIL.BAT = Venus (Abendstern), Shamash = Sonne, Tanzendes Wildschaf = Merkur, Stetiges Wildschaf = Saturn. </gallery> Wenn ein anhand der Beobachtung der Mondphasen ein leicht zu führender '''Mondkalender''' ('''Lunarkalender''') verwendet wird, verschieben sich die Tag-und-Nacht-Gleiche respektive der Frühlingsanfang und der Herbstanfang sowie die Sonnenwenden jährlich um knapp '''elf Tage''' nach hinten, weil immer nur zwölf synodische Monate (jeweils von Neulicht zu Neulicht mit zirka 29,53&nbsp;Tagen) beziehungsweise zusammengenommen rund 354&nbsp;Tage berücksichtigt werden. Das tropische Sonnenjahr hat jedoch gut 365&nbsp;Tage, dauert also ungefähr elf Tage länger als zwölf synodische Monate. Um aus dem Lunarkalender einen '''Lunisolarkalender''' zu machen, der mit dem tropischen Sonnenjahr im Einklang bleibt, haben schon die alten Babylonier ungefähr alle drei Jahre einen zusätzlichen 13.&nbsp;synodischen '''Schaltmonat''' eingefügt. Dies wird in der Zeitrechnung auch als '''Interkalation''' bezeichnet (lateinisch "intercalatio", zu Deutsch: „Zwischenschaltung“). Der '''Julianische''' und der '''Gregorianische Kalender''' sind reine '''Sonnenkalender''' ('''Solarkalender'''), bei denen der Frühling immer um den 21.&nbsp;März beginnt. Hier wird die Synchronität der zwölf Monate mit dem Jahreszyklus dadurch hergestellt, dass die Monate 30&nbsp;oder 31&nbsp;Tage haben, also länger als ein synodischer Monat dauern. Der letzte Monat des ursprünglichen Sonnenjahres, der Februar, hat als einziger Monat 28&nbsp;Tage. Da das tropische Sonnenjahr nicht exakt 365&nbsp;Tage hat, sondern knapp einen Vierteltag länger dauert, wird im '''Julianischen Kalender''' alle vier Jahre (Jahreszahl ohne Rest durch vier teilbar) als letzter Tag des Jahres ein 29.&nbsp;Februar als '''Schalttag''' eingefügt: :<math>\left( 365 + \frac {1} {4} \right) \text {Tage} = 365,25 \text { Tage}</math> Durch die gregorianische Kalenderreform im Jahr 1582 wurde dem Umstand Rechnung getragen, dass die Differenz zwischen dem tropischen Sonnenjahr und 365&nbsp;ganzen Tagen nicht exakt einen Vierteltag beträgt, sondern etwas weniger, nämlich nur rund 0,24219&nbsp;Tage. Da sich nach fast 16&nbsp;Jahrhunderten die Differenz zwischen Frühlingsanfang und dem 21.&nbsp;März auf zehn Tage aufsummiert hatte, wurde beim Wechsel vom Julianischen Kalender zum '''Gregorianischen Kalender''' zum einen um zehn Tage nach vorne gesprungen, und zum anderen wurde nicht mehr alle vier Jahre ein Schaltjahr eingeschoben, sondern alle einhundert Jahre wurde das Schaltjahr weggelassen und alle vierhundert Jahre wiederum nicht weggelassen. Die Jahreslänge nährt sich dem tropischen Sonnenjahr damit deutlich besser an und beträgt im Mittel: :<math>\left( 365 + \frac {1} {4} - \frac {1} {100} + \frac {1} {400} \right) \text {Tage} = \left( 365 + \frac {100} {400} - \frac {4} {400} + \frac {1} {400} \right) \text {Tage} = \left( 365 + \frac {97} {400} \right) \text {Tage} = 365,2425 \text { Tage}</math> Damit wird das tropische Sonnenjahr auf weniger als eine Minute genau angenähert. Das mittlere Gregorianische Jahr dauert derzeit nur zirka 27&nbsp;Sekunden länger als das tropische Sonnenjahr. Da die Differenz zwischen drei tropischen Jahren (1095,7&nbsp;Tage) und 37&nbsp;synodischen Monaten (1092,6&nbsp;Tage) nicht Null, sondern gut drei Tage beträgt, muss auch bei der Anwendung einer Drei-Jahres-Regel gelegentlich eine Korrektur angebracht werden. Diese führt dazu, dass manchmal nicht schon nach drei Jahren, sondern erst am Ende des vierten Jahres ein dreizehnter synodischer Schaltmonat eingefügt wird. Die Plejaden-Schaltregeln sind zu diesem Zweck sehr hilfreich, da sie das Mondalter in den ersten Tagen des Neulichts mit der ekliptikalen Länge der Sonne im '''Frühlingspunkt''' in Bezug setzt und somit dafür sorgt, dass der '''Frühlingsvollmond''' stets in zeitlicher Nähe zur Tag-und-Nacht-Gleiche im Frühjahr stattfindet. Die beiden überlieferten babylonische Plejaden-Schaltregeln sind in den Zeilen acht bis elf der zweiten Tontafel der MUL.APIN-Serie festgehalten worden. Die Keilschriftzeichen der Anfänge von Zeile zehn und elf sind zwar nicht vollständig erhalten, können jedoch relativ zuverlässig und sinngebend rekonstruiert werden:<ref name="Papke" /> <blockquote> 8 Wenn am '''ersten Nisannu''' Plejaden und Mond sich die Waage halten, ist dieses Jahr '''normal'''.<br/> 9 Wenn am '''dritten Nisannu''' Plejaden und Mond sich die Waage halten, ist dieses Jahr '''voll'''.<br/> 10 Wenn am '''ersten Ajaru''' die Plejaden aufgehen, ist dieses Jahr '''normal'''.<br/> 11 Wenn am '''ersten Simanu''' die Plejaden aufgehen, ist dieses Jahr '''voll'''. </blockquote> Sowohl die Zeilen 8 und 9 als auch die Zeilen 10 und 11 stellen unabhängig voneinander jeweils eine Regel dar, nach welcher das Jahr bestimmt werden kann, in dem ein Schaltmonat einzuführen ist. Auf den seit etwa 2600 vor Christus überlieferten akkadischen Keilschrifttafeln finden sich viele noch ältere sumerische Lehnwörter insbesondere für Himmelsobjekte. '''Nisannu''', '''Ajaru''' und '''Simanu''' sind die akkadischen Namen für die ersten drei synodischen Monate des babylonischen Kalenders. Akkadisch ist die älteste semitische Sprache, die zahlreiche spätere semitische Sprachen beeinflusst hat, wie zum Beispiel Phönizisch, Aramäisch, Syrisch, Arabisch, Maltesisch, aber auch Hebräisch. Die akkadischen Monatsnamen finden sich deswegen in sehr ähnlicher Form auch im religiösen Lunisolarkalender des Judentums wieder: '''Nisan''', '''Ijar''' und '''Siwan'''. [[Datei:Jahreskreis.Fruehlingspunkt.png|mini|links|hochkant=1.5|Der Jahreskreis mit den akkadischen (außen) und gregorianischen (innen) Monaten vom Frühlingspunkt (oben) aus. Die ekliptikale Länge der Sonne <math>\lambda</math> startet beim Frühlingspunkt mit dem Wert Null und nimmt in der Darstellung im Uhrzeigersinn zu. Zwölf akkadische Monate sind um elf Tage kürzer als ein Sonnenjahr.]] {| class="wikitable" !Monatsnummer !Babylonischer Kalender !Jüdischer Kalender !Entspricht im Solarkalender |- |1 |Nisannu |Nisan |März |- |2 |Ajaru |Ijar |April |- |3 |Simanu |Siwan |Mai |- |4 |Duuzu |Tammuz |Juni |- |5 |Abu |Av |Juli |- |6 |Ululu |Elul |August |- |7 |Tašritu |Tischri |September |- |8 |Araḫsamna |Cheschwan |Oktober |- |9 |Kislimu |Kislew |November |- |10 |Tebetu |Tevet |Dezember |- |11 |Šabatu |Schevat |Januar |- |12 |Addaru |Adar |Februar |} Der erste Monat Nisannu hieß bei den Bewohnern von Nippur auf Sumerisch "bara-zag-gar", was als "beim glänzenden Thron/Herrscher" gedeutet werden kann (bara = Thron, zag = Grenze / Seite, gar = Erscheinung / Ochse der Sonne).<ref>John A. Halloran: [https://www.sumerian.org/sumerian.pdf Sumerian Lexicon], version 3.0</ref> Das war zur Zeit der Sumerer demnach der Monat, in welchem die Sonne beim Himmelsthron stand, der wiederum mit dem großen Sternbild Himmelsstier identifiziert werden kann, wo sich der Frühlingspunkt damals befand. Der zweite Monat Ajaru hieß auf Sumerisch "gu(d)-si-su", was als "der gehörnte Stier ist ersetzt" gedeutet werden kann (gud = Stier, si = Hörner , su = ersetzen / schwächer werden). Dies ist ein Hinweis darauf, dass die Sonne den Himmelsstier und somit ihren Frühlingspunkt in diesem Monat bereits hinter sich gelassen hatte. Ein normales Jahr hat zwölf synodische Monate und ein volles (respektive übergroßes) Jahr wird um einen Schaltmonat erweitert. Nach dem zwölften Monat des Jahres mit der Bezeichnung Addaru (hebräisch '''Adar''') gab es dann noch einen 13.&nbsp;Monat mit der Bezeichnung Addaru&nbsp;II. Im Hebräischen heißen diese beiden Monate '''Adar aleph''' ("Adar A") oder '''Adar rischon''' (erster Adar) sowie '''Adar beth''' ("Adar B") oder '''Adar scheni''' (zweiter Adar). [[Datei:Konjunktion.Neulicht.Merkur.Plejaden.P1138787.jpg|mini|rechts|hochkant=2|Konjunktion der zunehmenden '''Mondsichel''' (dreieinhalb Prozent Neulicht (Mondalter 1,8&nbsp;Tage, östliche Elongation 21,5&nbsp;Bogengrad) mit aschgrauem Mondlicht durch den Erdschein beim Abenderst, links) mit dem Planeten Merkur (scheinbare Helligkeit 1,5<sup>m</sup>, Bildmitte) und dem offenen Sternhaufen der '''Plejaden''' im Sternbild Stier (Taurus, rechts) am 2.&nbsp;Mai 2022 (nach dem jüdischen Lunarkalender der 1.&nbsp;Siwan 5782) ungefähr 8,5&nbsp;Bogengrad über dem westnordwestlichen Abendhimmel in Berlin.]] "Sich die Waage halten" bedeutet, dass die Plejaden und die Mondscheibe in '''Konjunktion''' stehen. Hierbei bleibt offen, welche der beiden üblichen Definitionsmöglichkeiten für eine Konjunktion bei den Babyloniern zur Anwendung gekommen war, denn: * Entweder handelt es sich um das Erreichen der gleichen Horizonthöhe im '''horizontalen Koordinatensystem''', → siehe [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Der_Horizont|'''Der Horizont''']]. * Oder es handelt es sich um das Erreichen der gleichen ekliptikalen Länge im '''ekliptikalen Koordinatensystem''', → siehe [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Die_Ekliptik|'''Die Ekliptik''']]. Die Wahl der Definition hat jedoch keine große Auswirkung auf die Datierung der astronomischen Ereignisse, da sie über dem westlichen Horizont am Frühlingsabend beide innerhalb von nur wenigen Stunden auftreten. Ferner kann berücksichtigt werden, dass die Ekliptik beim Untergang der Plejaden in Mesopotamien fast senkrecht auf dem Horizont steht und es deswegen in diesem Himmelsausschnitt nur zu sehr geringen Unterschieden zwischen den Differenzen der Höhenwinkel oder der ekliptikalen Längen kommt. Zur Interpretation der beiden Plejaden-Schaltregeln sind noch einige weitere Punkte als bekannt vorauszusetzen: * Alle Monate beginnen an dem Tag mit dem '''Neulicht''' des Mondes beim '''Abenderst''' über dem westlichen Horizont. Hier hat die Mondsichel ein Mondalter von ein bis zwei Tagen nach Neumond, und der Mond hat demzufolge in Bezug auf die Sonne eine östliche Elongation von ungefähr 15&nbsp;bis 25&nbsp;Bogengrad erreicht. * Mit "aufgehen" ist der '''heliakische Aufgang''' der Plejaden am östlichen Horizont beim '''Morgenerst''' gemeint, nachdem sie nach ihrem '''Abendletzt''' beim '''akronychischen Untergang''' ungefähr vierzig Tage lang nicht beobachtet werden konnten, weil sie während dieser Zeit zu sehr in Sonnennähe standen und von der Sonne überstrahlt wurden, während die Sonne prograd (rechtläufig) an ihnen vorbeigezogen ist. Da der offene Sternhaufen der Plejaden eine nördliche ekliptikale Breite von ungefähr vier Bogengrad hat, steht die Sonnenscheibe nach zwanzig Tagen der Unsichtbarkeit der Plejaden bei der Konjunktion von Sonne und Plejaden vier Bogengrad südlich von diesem Sternhaufen. * Die Tag-und-Nacht-Gleiche im Frühjahr ('''Frühlingsäquinoktium''') wurde von den Babyloniern auf den ersten Kalendermonat '''Nisannu''' festgelegt, an dem sich in einem "normalen" Jahr in der Mitte des Monats der erste '''Vollmond''' des Jahres zeigt. Ein ähnlicher Ansatz mit dem Frühlingsvollmond gilt heute noch für die Festlegung des jüdischen '''Pessach-Festes''', das am '''Seder''', dem Vorabend des '''15.&nbsp;Nisan''' beginnt, beziehungsweise für die Festlegung des christlichen '''Osterfestes''', das am Sonntag nach dem ersten Frühlingsvollmond stattfindet. ** → Siehe auch [[Astronomie von der Frühgeschichte bis zur Neuzeit/ Kalenderführung/ Osterdatum|'''Osterdatum''']]. Im tatsächlichen Frühlingsäquinoktium hat die Sonne in allen Epochen exakt die ekliptikale Länge Null. Ein gleichzeitig auftretender Neumond hat dann ebenfalls die ekliptikale Länge Null. Da nach der babylonischen Definition der Frühlingsbeginn jedoch immer mit dem Vollmond in der Mitte des Monats zusammenfällt, befindet sich die prograd (rechtläufig) entlang der Ekliptiklinie laufende Sonne zu Beginn des Monats, also rund 14&nbsp;Tage vorher noch mit der Differenz <math>\Delta\lambda_{Sonne,14d}</math> bei einer kleineren ekliptikalen Länge vor dem Frühlingspunkt von ungefähr :<math>\Delta\lambda_{Sonne,14d} \approx 14 \text { Tage} \cdot \frac {360^\circ} {365,25 \text { Tage}} \approx 13,8^\circ</math> beziehungsweise :<math>\Delta\lambda_{Sonne,1d} \approx 1 \text { Tag} \cdot \frac {360^\circ} {365,25 \text { Tage}} \approx 0,986^\circ</math>, wenn eine Jahreslänge von 365,25&nbsp;Tagen angesetzt wird. Der Mond bewegt sich ebenfalls ständig prograd (rechtläufig) entlang der Ekliptik, wobei er innerhalb eines siderischen Monats 360&nbsp;Bogengrad durchläuft. Da ein siderischer Monat die Dauer von 27,322&nbsp;Tagen hat, ergibt sich eine mittlere tägliche Mondbewegung <math>\Delta\lambda_{Mond,1d}</math> von: :<math>\Delta\lambda_{Mond,1d} \approx 1 \text { Tag} \cdot \frac {360^\circ} {27,322 \text { Tage}} \approx 13,2^\circ</math> Der Mond bewegt sich an einem Tag also fast genauso weit wie die Sonne in zwei Wochen. Die folgende Tabelle gibt an, wie sich die Mondsichel in den ersten Tagen nach Neumond (Mondalter gleich Null) im Mittel entwickelt, wenn sowohl Mond als auch Sonne zum Startzeitpunkt im Frühlingspunkt stehen (ekliptikale Länge gleich Null): {| class="wikitable" |+ Mondphasen nach Neumond ! Mondalter<br/>in Tagen !! Ekliptikale Länge<br/>Sonne<br/>in Bogengrad !! Mondsichel<br/>in Prozent !! Ekliptikale Länge<br/>Mond<br/>in Bogengrad || Östliche Elongation<br/>des Mondes<br/>in Bogengrad || Anzahl der Tage<br/>bis die Sonne<br/>die ekliptikale Länge<br/>des Mondes erreicht |- | 0,0 || 0,0 || 0,0 || 0,0 || 0,0 || 0 |- | 0,5 || 0,5 || 0,3 || 6,6 || 6,1 || 6 |- | 1,0 || 1,0 || 1,1 || 13,2 || 12,2 || 12 |- | 1,5 || 1,5 || 2,5 || 19,8 || 18,3 || 19 |- | 2,0 || 2,0 || 4,5 || 26,4 || 24,4 || 25 |- | 2,5 || 2,5 || 6,9 || 32,9 || 30,5 || 31 |- | 3,0 || 3,0 || 9,8 || 39,5 || 36,6 || 37 |- | 3,5 || 3,4 || 13,2 || 46,1 || 42,7 || 43 |- | 4,0 || 3,9 || 17,0 || 52,7 || 48,8 || 49 |} Durch die schwankenden Bahngeschwindigkeiten von Mond und Erde können sich je nach betrachtetem Jahr Abweichungen von diesen mittleren Werten ergeben. Die Plejaden können mit ihrer scheinbaren Helligkeit von 1,5<sup>m</sup> freiäugig erst beobachtet werden, wenn sie gut zwei Bogenengrad über dem Horizont stehen. Die Sonne muss gleichzeitig (zum Ende der astronomischen Dämmerung) schon tief genug unter dem Horizont stehen, um durch ihr Streulicht in der Atmosphäre das Sternenlicht nicht zu überstrahlen. === Die erste Schaltregel === Für den ersten mesopotamischen Monat Nisannu stellt sich die Situation anhand der ersten Plejaden-Schaltregel in drei aufeinanderfolgenden Jahren auf der geographischen Breite von Babylon folgendermaßen dar: [[Datei:Plejaden-Schaltregel.png|mini|links|hochkant=4|Die '''Plejaden-Schaltregel''' im Monat Nisannu beim abendlichen Untergang von '''Mond''' und '''Plejaden''' (weißer Sternhaufen) im Westen im Sternbild Stier (Taurus). Die '''Sonne''' (gelb) ist zu diesem Zeitpunkt bereits untergegangen und steht so weit unter dem '''Horizont''' (dunkelgrün), dass hellere Sterne zu sehen sind. Die '''Ekliptik''' (rot gepunktet) steht in Mesopotamien um die Tag-und-Nacht-Gleiche im Frühjahr abends fast senkrecht auf dem westlichen Horizont:<br/> -&nbsp;Im Jahr X ist das '''Neulicht''' des Mondes beim akronychischen Untergang zum Abenderst am '''1.&nbsp;Nisannu''' noch gerade so über dem westlichen Horizont neben den Plejaden zu sehen. Dieser Zeitpunkt fällt mit dem akronychischen Untergang der Plejaden bei ihrem Abendletzt zusammen. Dieses Jahr gilt im babylonischen Kalender als ein '''normales Jahr'''.<br/> -&nbsp;Im Jahr X steht die Sonne am '''15.&nbsp;Nisannu''' neben den (nicht sichtbaren) Plejaden im '''Frühlingspunkt''' (Epoche J-2600) im Sternbild '''Himmelsstier''' (Taurus und Aries). An diesem Tag herrscht der '''Frühlingsvollmond''', der zur dargestellten Tageszeit nach Sonnenuntergang am Abend im Osten gerade eben im gegenüberliegenden Herbstpunkt im Sternbild Himmelsskorpion (Scorpio und Libra) bei den Sternen Akrab (β Scorpii) und Dschubba (δ Scorpii) knapp zehn Bogengrad westlich vom hellen Roten Überriesen Antares (α Scorpii) und ungefähr eine dreiviertel Stunde vor diesem aufgegangen ist (siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Der_Himmelsstier|Abschnitt '''Himmelsstier''']] und [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Der_Himmelsskorpion|Abschnitt '''Himmelsskorpion''']]).<br/> -&nbsp;Im Jahr X+1 steht der Mond beim Neulicht einen Tag weiter in Richtung Osten von der Sonne entfernt, und die Mondsichel steht erst am '''2.&nbsp;Nisannu''' neben den Plejaden.<br/> -&nbsp;Im Jahr X+2 steht der Mond beim Neulicht zwei Tage weiter in Richtung Osten von der Sonne entfernt, und die Mondsichel steht dann am '''3.&nbsp;Nisannu''' neben den Plejaden. Am Ende dieses Jahres wird nach dem 12.&nbsp;Monat Addaru ein 13.&nbsp;'''Schaltmonat''' mit der Bezeichnung Addaru&nbsp;II eingefügt, um Mondjahre und Sonnenjahr wieder zu synchronisieren. Dieses Jahr gilt im babylonischen Kalender als ein '''volles (übergroßes) Jahr'''.]] <div style="clear:both"></div> === Die zweite Schaltregel === [[Datei:Plejaden-Schaltregel.2.png|mini|rechts|hochkant=2|Die '''Plejaden-Schaltregel''' am östlichen Horizont (dunkelgrün, Ekliptik rot gepunktet) anhand der Beobachtung des heliakischen Aufgangs der Plejaden. Die '''Sonne''' (gelb) befindet sich noch 12&nbsp;Bogengrad unterhalb des Horizonts:<br/> -&nbsp;Im '''normalen''' Jahr findet der heliakische Aufgang der Plejaden einen Monat später als der akronychische Untergang (Abendletzt) am 1.&nbsp;Nisannu statt, also am '''1.&nbsp;Ajaru''' (Neulicht des Mondes am Abend).<br/> -&nbsp;Ein Jahr später findet der heliakische Aufgang der Plejaden am '''15.&nbsp;Ajaru''' statt, während der gleichzeitig auftretende '''Vollmond''' gegenüber im Westen untergeht.<br/> -&nbsp;Zwei Jahre später, im '''vollen (übergroßen) Jahr''', findet der heliakische Aufgang der Plejaden einen Monat später als im normalen Jahr statt, nämlich am '''1.&nbsp;Simanu''' (Neulicht des Mondes am Abend). Am Ende dieses Jahres wird nach dem 12.&nbsp;Monat Addaru ein 13.&nbsp;'''Schaltmonat''' mit der Bezeichnung Addaru&nbsp;II eingefügt, um Mondjahre und Sonnenjahre wieder zu synchronisieren.]] Die zweite Plejaden-Schaltregel betrachtet ebenfalls in diesem Dreijahreszeitraum, aber unabhängig von einer Konjunktion zwischen Mond und Plejaden den '''heliakischen Aufgang''' der Plejaden über dem östlichen Horizont beim '''Morgenerst'''. Die Ekliptik steht in Mesopotamien um die Tag-und-Nacht-Gleiche im Frühjahr morgens deutlich flacher zum Horizont als abends, so dass der Aufgang der Plejaden nicht so schnell erfolgt wie der Untergang. === Die dritte Schaltregel === Ferner ist darauf hinzuweisen, dass weitere Schaltregeln bekannt sind, wie zum Beispiel diejenige, die sich auf den letzten beobachtbaren Aufgang des Sterns Sirius in der Abenddämmerung um die Sonnenwende im Winter bezieht, die in den babylonischen Monaten Tebetu oder Šabatu stattfand. Der sumerische Name des hellsten Sterns am Nachthimmel lautet KAK.SI.SÁ, was so viel wie "Himmelspfeil" bedeutet. Der Sirius ging in Mesopotamien vor dreitausend Jahren bei einem Azimut von rund 110&nbsp;Bogengrad (also in der Himmelsrichtung Ostsüdost) auf. Heute ist dies bei einem Azimut von ungefähr 104&nbsp;Bogengrad der Fall, und die Drift des Azimuts betrug also nur zirka zwei Bogengrad pro Jahrtausend. Die Schaltregel besagt nach den Keilschrifttexten der MUL.APIN-Tafeln, aber auch nach anderen babylonischen Quellen sinngemäß: <blockquote> Wenn Sirius am Abend des 15. Tebetu aufgeht, ist dieses Jahr '''normal'''.<br/> Wenn Sirius am Abend des 15. Šabatu aufgeht, ist dieses Jahr ein '''Schaltjahr'''. </blockquote> An den 15.&nbsp;Tagen aller babylonischen Monate herrschte stets Vollmond. Der Vollmond befindet sich um Mitternacht dann stets auf dem südlichen Meridian. Der Aufgang des Sirius findet in Mesopotamien ungefähr gleichzeitig mit der oberen Kulmination des Herbstvollmonds auf dem südlichen Meridian (also gegen Mitternacht) statt. In den drei folgenden Monaten kann der Aufgang von Sirius zunehmend früher in der ersten Nachthälfte beobachtet werden, bevor nach der Wintersonnenwende das dann wieder länger anhaltende Tageslicht seinen Aufgang überstrahlt, so dass er erst nach Eintritt der Abenddämmerung in immer weiter westlich liegenden Richtungen gesehen werden kann. === Wanderung des Frühlingspunkts === Aus den Fakten ergibt sich unter der Berücksichtigung des Wanderns der Frühlingspunktes durch den vollen Kreis der Ekliptik innerhalb von rund 25800&nbsp;Jahren, dass die beiden von den MUL.APIN-Tafeln bekannten oben zuerst genannten Plejaden-Schaltregeln der Babylonier bereits in der Mitte des dritten Jahrtausends vor Christus ihre Gültigkeit hatten und demzufolge zu diesem Zeitpunkt schon bekannt und in Verwendung gewesen sein muss.<ref name="Papke" /> Der Frühlingspunkt befand sich zur Epoche J-2600 im [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Das_Goldene_Tor_der_Ekliptik|Goldenen Tor der Ekliptik]] im heutigen Sternbild Stier (Taurus, die Sumerer kannten ihn als '''Himmelsstier'''), und der Hauptstern der Plejaden, '''Alkyone''', hatte damals eine ekliptikale Länge von 356,4&nbsp;Bogengrad. Zum Zeitpunkt der Herstellung der überlieferten MUL.APIN-Tafeln war der Frühlingspunkt schon erheblich weiter in Richtung Sternbild Widder (Aries) gewandert (dieser wird seitdem und deswegen auch als '''Widderpunkt''' bezeichnet), so dass die Plejaden-Schaltregeln in der Praxis dann keine Gültigkeit mehr hatten und in der überlieferten Form gar nicht mehr angewendet werden können. Wir dürfen folglich davon ausgehen, dass sich die Gelehrten in Mesopotamien über die Jahrhunderte lang beobachtete Verschiebung des Frühlingspunkts spätestens im zweiten Jahrtausend vor Christus im Klaren waren. Inzwischen befindet sich der Frühlingspunkt noch ein Sternbild weiter westlich, nämlich im Sternbild Fische (Pisces). === Schaltrechnung === Aus den babylonischen Schaltregeln ergibt sich, dass innerhalb von '''19''' Sonnenjahren '''7''' zusätzliche synodische Monate nach jeweils '''12''' synodischen Monaten eingeschaltet werden müssen, damit Sonnen- und Mondjahr langfristig synchron bleiben: * Tropisches Sonnenjahr: <math>J_S = 365,242 \text { d}</math> * Synodischer Monat: <math>M_{syn} = 29,531 \text { d}</math> * Synodisches Jahr: <math>J_{syn} = 12 \cdot M_{syn} = 354,372 \text { d}</math> * Jahresdifferenz: <math>\Delta J = J_S - J_{syn} = 10,870 \text { d}</math> * Meton-Zyklus: <math>19 \cdot \Delta J = 206,53 \text { d}</math> beziehungsweise <math>7 \cdot M_{syn} = 206,72 \text { d}</math> Mit anderen Worten war der Sachverhalt des [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Mondzyklen#Der_Meton-Zyklus|'''Meton-Zyklus''']] damals schon bekannt und somit bereits über zwei Jahrtausende bevor sich der griechische Astronom Meton damit beschäftigte. Ein weiteres Indiz ist die Tatsache, dass das sumerische Neujahrsfest beim Übergang vom alten Mondjahr zum nächsten Nisannu elf Tage lang gefeiert wurde, was genau der geringfügig aufgerundeten Jahresdifferenz <math>\Delta J</math> entspricht. → Siehe hierzu auch: [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Kalenderführung#Das_babylonische_Neujahrsfest_Akiti|'''Das babylonische Neujahrsfest Akiti''']]. Die also noch auf die Sumerer zurückgehenden und einfach zu befolgenden Schaltregeln wurden vermutlich zu Beginn des ersten vorchristlichen Jahrtausends aus dem Neuassyrischen Großreich in das Nordreich Israel gebracht, wo sie die Ausgestaltung des jüdischen Kalenders wesentlich beeinflusst haben.<ref>Matthias Albani: [https://www.die-bibel.de/ressourcen/wibilex/altes-testament/sterne-sternbilder-sterndeutung Sterne / Sternbilder / Sterndeutung / Orion (כְּסִיל) und Plejaden / Siebengestirn (כִּימָה)], WiBiLex, das wissenschaftliche Bibellexikon im Internet, September 2014</ref> === Zahlenverhältnisse === [[Datei:BlumeDesLebens19.png|mini|rechts|hochkant=1|Hexagonaler Ring mit insgesamt '''neunzehn''' jeweils um eine Radiuslänge überlappenden Kreisen: '''sieben''' Kreise im Innern und '''zwölf''' Kreise außen. Die '''eine''' Sonne im Zentrum, umgeben von den '''sechs''' weiteren Wandelgestirnen Mond, Merkur, Venus, Mars, Jupiter und Saturn, die wiederum von den '''zwölf''' Lebewesenkreiszeichen umgeben sind.]] Bemerkenswert ist auch der unmittelbare Zusammenhang zwischen den drei bedeutenden und heiligen Zahlen '''[[Quadriviale_Kuriositäten/_Zahlen#Zur_Sieben|Sieben]]''' und '''[[Quadriviale_Kuriositäten/_Zahlen#Zur_Zwölf|Zwölf]]''' sowie '''[[Quadriviale_Kuriositäten/_Zahlen#Zur_Neunzehn|Neunzehn]]''', die sich im Übrigen auch in der nebenstehenden einfach zu konstruierenden Kreisgraphik und in der folgenden Summenformel wiederfinden: :<math> 19 = 7 + 12 = 1 + 6 + 12 </math> Die '''Sieben''' und die '''Neunzehn''' sind Primzahlen, die für asymmetrische Zahlenverhältnisse stehen. Folgender Kettenbruch kann auf das Zahlenverhältnis von 19 zu 7 gekürzt werden und stellt als vierte Konvergente die beste Näherung der transzendenten (also auch irrationalen) reellen Eulerschen Zahl <math>e</math> dar: :<math> e \approx 2,718282 \approx 2 + \frac {1} { 1 + \frac {1} {2 + \frac {1} {1 + \frac {1} {1}}}} = 2 + \frac {1} { 1 + \frac {1} {2 + \frac {1} {2}}} = 2 + \frac {1} { 1 + \frac {2} {5}} = 2 + \frac {5} {7} = \frac {19} {7} = 2,\overline{714285} </math> Die '''Zwölf''' wiederum ist die kleinste hochzusammengesetzte Zahl mit sechs ganzzahligen Teilern sowie die Summe der drei Komponenten des kleinsten Pythagoreischen Tripels (3, 4,.5). Sie ist hochsymmetrisch und steht beispielsweise symbolisch sowohl für die '''zwei''' Jahreshälften mit je '''sechs''' Monaten als auch für die '''vier''' Jahreszeiten mit je '''drei '''Monaten: :<math> 19 = 7 + 12 = 7 + 3 \cdot 4 = 7 + 2 \cdot 6 = 7 + 3 \cdot 2 \cdot 2 = 7 + (3 + 4 + 5) = (3 + 4) + (3 + 4 + 5) = 2 \cdot 7 + 5 </math> Die Zwölf spiegelt sich unmittelbar in den '''zwölf''' Sternzeichen des '''einen''' Lebewesenkreises auf der Ekliptik sowie in den '''zwölf''' Monaten '''eines''' Jahres wider. Ferner zieht der Planet Jupiter in knapp '''zwölf''' Jahren '''einmal''' durch den Lebewesenkreis. Astronomische Symmetrien, die sich auf die Teiler der Zwölf beziehen, nämlich Zwei, Drei, Vier und Sechs, haben zahlreiche weitere Ausprägungen: * Die '''Zwei''' in astronomischen Kategorien: ** Sommerhalbjahr / Winterhalbjahr mit jeweils '''sechs''' Monaten ** Länger werdende Tage (Winter und Frühling) / kürzer werdende Tage (Sommer und Herbst) in jeweils '''sechs''' Monaten ** Zunehmender / abnehmender Mond ** Neumond / Vollmond ** Steigender / fallender Mond (obsigend und nidsigend) ** Große / kleine Mondwende ** Tag / Nacht mit jeweils '''zwölf''' Stunden ** Aufgang / Untergang ** Zenit / Nadir ** Äquinoktien: Frühlingspunkt / Herbstpunkt ** Solstitien: Sommersonnenwende / Wintersonnenwende ** Aufsteigender / absteigender Mondknoten ** Himmelsstier / Himmelsskorpion ** Die beiden inneren Planeten: Merkur und Venus, die rechts oder links der Sonnenscheibe zu sehen sind ** Die beiden scheibenförmigen Wandelgestirne: Sonne und Mond * Die '''Vier''' in astronomischen Kategorien: ** Jahreszeiten: Frühling, Sommer, Herbst und Winter mit jeweils '''drei''' Monaten ** Himmelsrichtungen: Osten, Süden, Westen und Norden ** Königssterne: Aldebaran, Regulus, Antares und Fomalhaut ** Hauptpunkte der Sonnenbahn: Frühlingspunkt, Sommersonnenwende, Herbstpunkt und Wintersonnenwende ** Tageszeiten: Morgen, Mittag, Abend und Mitternacht mit jeweils '''sechs''' Stunden ** Mondviertel: Neumond, zunehmender Halbmond, Vollmond und abnehmender Mond mit jeweils '''sieben''' Tagen ==Einzelnachweise== <references></references> 1dbd36cjirfkfuaeh0tuw0a1obw28ca Wikibooks:GUS2Wiki 4 116489 1088111 1087767 2026-06-13T18:05:36Z Alexis Jazz 96587 Updating gadget usage statistics from [[Special:GadgetUsage]] ([[phab:T121049]]) 1088111 wikitext text/x-wiki {{#ifexist:Project:GUS2Wiki/top|{{/top}}|This page provides a historical record of [[Special:GadgetUsage]] through its page history. To get the data in CSV format, see wikitext. To customize this message or add categories, create [[/top]].}} Diese Daten stammen aus dem Cache. Der Zeitpunkt der letzten Aktualisierung: 2026-06-13, 07:18:37Z Uhr. Maximal {{PLURAL:5000|ein Ergebnis ist|5000 Ergebnisse sind}} im Cache verfügbar. {| class="sortable wikitable" ! Helferlein !! data-sort-type="number" | Anzahl der Benutzer !! data-sort-type="number" | Aktive Benutzer |- |CollapseAll || 31 || 2 |- |HotCat || 27 || 1 |- |Pfeil-hoch || 80 || 3 |- |displayRealTitle || 18 || 2 |- |editSectionLink || 115 || 2 |- |erstelleSammlung || 89 || 1 |- |extra-editbuttons || 199 || 2 |- |mfnf-linter || data-sort-value="Infinity" | Standard || data-sort-value="Infinity" | Standard |- |navigation-popups || 25 || 1 |- |serlo-design || data-sort-value="Infinity" | Standard || data-sort-value="Infinity" | Standard |- |setupTitle || 53 || 1 |- |showAnchors || 42 || 4 |- |wikEd || 175 || 2 |} * [[Spezial:GadgetUsage]] * [[m:Meta:GUS2Wiki/Script|GUS2Wiki]] <!-- data in CSV format: CollapseAll,31,2 HotCat,27,1 Pfeil-hoch,80,3 displayRealTitle,18,2 editSectionLink,115,2 erstelleSammlung,89,1 extra-editbuttons,199,2 mfnf-linter,default,default navigation-popups,25,1 serlo-design,default,default setupTitle,53,1 showAnchors,42,4 wikEd,175,2 --> q1hj79whsrt4z1996n5bib57lfnvyju Ing Mathematik: Python 0 117969 1088098 1088096 2026-06-13T13:07:39Z Intruder 1513 /* Objektorientierte Programmierung */ 1088098 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 Python-Code: wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung ist aus anderen Programmiersprachen bereits bekannt. In Pseudocode lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklasse) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odelling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das Klassendiagramm verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: [[File:InheritancePgmUML.svg|200px]] Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} lgpvd8sgol04m3wcfph9k07pc4ahtkr 1088099 1088098 2026-06-13T13:10:13Z Intruder 1513 /* Objektorientierte Programmierung */ 1088099 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 Python-Code: wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung ist aus anderen Programmiersprachen bereits bekannt. In Pseudocode lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odelling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das Klassendiagramm verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: [[File:InheritancePgmUML.svg|200px]] Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} 13gxl764a0jw5kflzezr74sgoi5y4fr 1088100 1088099 2026-06-13T14:34:45Z Intruder 1513 /* Objektorientierte Programmierung */ Link zu WP hinzu 1088100 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 Python-Code: wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung ist aus anderen Programmiersprachen bereits bekannt. In Pseudocode lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = {{Wikipedia|Objektorientierte Programmierung}} * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odelling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das Klassendiagramm verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: [[File:InheritancePgmUML.svg|200px]] Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} d2rpk0xv27zp78xvy5wbkvxpn6t2d8u 1088101 1088100 2026-06-13T16:13:49Z Intruder 1513 /* if */ 1088101 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 Python-Code: wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung sei aus anderen Programmiersprachen bereits bekannt. In Pseudocode lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE Als UML-Aktivitätsdiagramm sieht das in etwa so aus: [[File:If-Then-Else-diagram.svg|200px]] Und als {{W|Nassi-Shneiderman-Diagramm|Nassi-Shneiderman-Struktogramm}} so: [[File:Zweiseitige Auswahl.png|250px]] In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = {{Wikipedia|Objektorientierte Programmierung}} * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odelling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das Klassendiagramm verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: [[File:InheritancePgmUML.svg|200px]] Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} gvj2emfor0ubmzl7h0o0tb2wxywiez4 1088102 1088101 2026-06-13T16:19:15Z Intruder 1513 /* match */ 1088102 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 Python-Code: wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung sei aus anderen Programmiersprachen bereits bekannt. In Pseudocode lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE Als UML-Aktivitätsdiagramm sieht das in etwa so aus: [[File:If-Then-Else-diagram.svg|200px]] Und als {{W|Nassi-Shneiderman-Diagramm|Nassi-Shneiderman-Struktogramm}} so: [[File:Zweiseitige Auswahl.png|250px]] In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Als Struktogramm sieht das in etwa so aus: [[File:Mehrseitige Auswahl.png|250px]] Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = {{Wikipedia|Objektorientierte Programmierung}} * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odelling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das Klassendiagramm verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: [[File:InheritancePgmUML.svg|200px]] Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} lhft3sc9hstpokmxdt4c4ql7qhxcffl 1088103 1088102 2026-06-13T16:24:38Z Intruder 1513 /* for */ 1088103 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 Python-Code: wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung sei aus anderen Programmiersprachen bereits bekannt. In Pseudocode lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE Als UML-Aktivitätsdiagramm sieht das in etwa so aus: [[File:If-Then-Else-diagram.svg|200px]] Und als {{W|Nassi-Shneiderman-Diagramm|Nassi-Shneiderman-Struktogramm}} so: [[File:Zweiseitige Auswahl.png|250px]] In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Als Struktogramm sieht das in etwa so aus: [[File:Mehrseitige Auswahl.png|250px]] Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === Struktogramm einer for-Schleife: [[File:Zählschleife.png|200px]] In Python bspw. so: for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = {{Wikipedia|Objektorientierte Programmierung}} * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odelling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das Klassendiagramm verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: [[File:InheritancePgmUML.svg|200px]] Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} cp6q6ko7qkuo670v84j3tdx78ysx5xm 1088114 1088103 2026-06-13T18:16:52Z Intruder 1513 /* Strings und Platzhalter */ bisschen umgeordnet 1088114 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals, Beispiel 1): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Python-Code (formatted string literals, Beispiel 2): wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung sei aus anderen Programmiersprachen bereits bekannt. In Pseudocode lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE Als UML-Aktivitätsdiagramm sieht das in etwa so aus: [[File:If-Then-Else-diagram.svg|200px]] Und als {{W|Nassi-Shneiderman-Diagramm|Nassi-Shneiderman-Struktogramm}} so: [[File:Zweiseitige Auswahl.png|250px]] In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Als Struktogramm sieht das in etwa so aus: [[File:Mehrseitige Auswahl.png|250px]] Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === Struktogramm einer for-Schleife: [[File:Zählschleife.png|200px]] In Python bspw. so: for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = {{Wikipedia|Objektorientierte Programmierung}} * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odelling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das Klassendiagramm verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: [[File:InheritancePgmUML.svg|200px]] Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} 073puo8b2f6e4wyewjivdej7h5wn85b 1088115 1088114 2026-06-13T18:23:22Z Intruder 1513 /* UML */ Tippfehler 1088115 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals, Beispiel 1): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Python-Code (formatted string literals, Beispiel 2): wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung sei aus anderen Programmiersprachen bereits bekannt. In Pseudocode lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE Als UML-Aktivitätsdiagramm sieht das in etwa so aus: [[File:If-Then-Else-diagram.svg|200px]] Und als {{W|Nassi-Shneiderman-Diagramm|Nassi-Shneiderman-Struktogramm}} so: [[File:Zweiseitige Auswahl.png|250px]] In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Als Struktogramm sieht das in etwa so aus: [[File:Mehrseitige Auswahl.png|250px]] Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === Struktogramm einer for-Schleife: [[File:Zählschleife.png|200px]] In Python bspw. so: for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = {{Wikipedia|Objektorientierte Programmierung}} * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odeling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das Klassendiagramm verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: [[File:InheritancePgmUML.svg|200px]] Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} rfnrys9jdzrutvuqwybe3xub32rh1xu 1088116 1088115 2026-06-13T18:26:01Z Intruder 1513 /* Verzweigungen */ 1088116 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals, Beispiel 1): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Python-Code (formatted string literals, Beispiel 2): wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung sei aus anderen Programmiersprachen bereits bekannt. In Pseudocode lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE Als {{W|Aktivitätsdiagramm|UML-Aktivitätsdiagramm}} sieht das in etwa so aus: [[File:If-Then-Else-diagram.svg|200px]] Und als {{W|Nassi-Shneiderman-Diagramm|Nassi-Shneiderman-Struktogramm}} so: [[File:Zweiseitige Auswahl.png|250px]] In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Als Struktogramm sieht das in etwa so aus: [[File:Mehrseitige Auswahl.png|250px]] Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === Struktogramm einer for-Schleife: [[File:Zählschleife.png|200px]] In Python bspw. so: for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = {{Wikipedia|Objektorientierte Programmierung}} * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odeling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das Klassendiagramm verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: [[File:InheritancePgmUML.svg|200px]] Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} 9ym6z39666xp5yx6vhdifytd52zjhrv 1088117 1088116 2026-06-13T18:27:59Z Intruder 1513 /* UML */ 1088117 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals, Beispiel 1): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Python-Code (formatted string literals, Beispiel 2): wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung sei aus anderen Programmiersprachen bereits bekannt. In Pseudocode lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE Als {{W|Aktivitätsdiagramm|UML-Aktivitätsdiagramm}} sieht das in etwa so aus: [[File:If-Then-Else-diagram.svg|200px]] Und als {{W|Nassi-Shneiderman-Diagramm|Nassi-Shneiderman-Struktogramm}} so: [[File:Zweiseitige Auswahl.png|250px]] In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Als Struktogramm sieht das in etwa so aus: [[File:Mehrseitige Auswahl.png|250px]] Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === Struktogramm einer for-Schleife: [[File:Zählschleife.png|200px]] In Python bspw. so: for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = {{Wikipedia|Objektorientierte Programmierung}} * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odeling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das {{W|Klassendiagramm}} verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: [[File:InheritancePgmUML.svg|200px]] Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} foryib07ntelvfkiugqy7wc24rltys8 1088132 1088117 2026-06-14T09:13:59Z Intruder 1513 /* UML */ 1088132 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals, Beispiel 1): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Python-Code (formatted string literals, Beispiel 2): wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung sei aus anderen Programmiersprachen bereits bekannt. In Pseudocode lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE Als {{W|Aktivitätsdiagramm|UML-Aktivitätsdiagramm}} sieht das in etwa so aus: [[File:If-Then-Else-diagram.svg|200px]] Und als {{W|Nassi-Shneiderman-Diagramm|Nassi-Shneiderman-Struktogramm}} so: [[File:Zweiseitige Auswahl.png|250px]] In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Als Struktogramm sieht das in etwa so aus: [[File:Mehrseitige Auswahl.png|250px]] Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === Struktogramm einer for-Schleife: [[File:Zählschleife.png|200px]] In Python bspw. so: for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = {{Wikipedia|Objektorientierte Programmierung}} * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odeling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das {{W|Klassendiagramm}} verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: <gallery> InheritancePgmUML.svg | Abgeleitete Klasse erbt von Basisklasse (Einfachvererbung) Diamond inheritance.svg | D erbt von B und C (Mehrfachvererbung). B und C erben von A (Einfachvererbung) </gallery> Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} ovxfd6rg00r80srhbsxdowkg9umo542 1088133 1088132 2026-06-14T10:43:36Z Intruder 1513 /* Doctests */ 1088133 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals, Beispiel 1): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Python-Code (formatted string literals, Beispiel 2): wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung sei aus anderen Programmiersprachen bereits bekannt. In Pseudocode lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE Als {{W|Aktivitätsdiagramm|UML-Aktivitätsdiagramm}} sieht das in etwa so aus: [[File:If-Then-Else-diagram.svg|200px]] Und als {{W|Nassi-Shneiderman-Diagramm|Nassi-Shneiderman-Struktogramm}} so: [[File:Zweiseitige Auswahl.png|250px]] In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Als Struktogramm sieht das in etwa so aus: [[File:Mehrseitige Auswahl.png|250px]] Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === Struktogramm einer for-Schleife: [[File:Zählschleife.png|200px]] In Python bspw. so: for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = {{Wikipedia|Objektorientierte Programmierung}} * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odeling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das {{W|Klassendiagramm}} verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: <gallery> InheritancePgmUML.svg | Abgeleitete Klasse erbt von Basisklasse (Einfachvererbung) Diamond inheritance.svg | D erbt von B und C (Mehrfachvererbung). B und C erben von A (Einfachvererbung) </gallery> Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] ''Einschub:'' Ganz ähnlich kann man auch Klassen testen, z.B. in folgendem Code-Fragment class Fahrzeug: # siehe Abschnitt "Objektorientierte Programmierung" # ... if __name__ == "__main__": vauweh = Fahrzeug(170, 90) beemweh = Fahrzeug(200, 120) print(vauweh.convertGeschw()) print(beemweh.convertGeschw()) Wird das Script als Hauptprogramm ausgeführt (z.B. zu Testzwecken), so erfolgt die Ausgabe der zwei, via <code>convertGeschw()</code>, umgerechneten Geschwindigkeiten. Wird es aber als Modul eingebunden, so wird der letzte Programmabschnitt nicht ausgeführt (<code>__name__ == "__main__"</code>) ergibt <code>False</code>. == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} ifvrj6ejbhz7fr65idx6weunq9ee0pq 1088135 1088133 2026-06-14T10:47:18Z Intruder 1513 /* Doctests */ 1088135 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals, Beispiel 1): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Python-Code (formatted string literals, Beispiel 2): wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung sei aus anderen Programmiersprachen bereits bekannt. In Pseudocode lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE Als {{W|Aktivitätsdiagramm|UML-Aktivitätsdiagramm}} sieht das in etwa so aus: [[File:If-Then-Else-diagram.svg|200px]] Und als {{W|Nassi-Shneiderman-Diagramm|Nassi-Shneiderman-Struktogramm}} so: [[File:Zweiseitige Auswahl.png|250px]] In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Als Struktogramm sieht das in etwa so aus: [[File:Mehrseitige Auswahl.png|250px]] Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === Struktogramm einer for-Schleife: [[File:Zählschleife.png|200px]] In Python bspw. so: for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = {{Wikipedia|Objektorientierte Programmierung}} * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odeling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das {{W|Klassendiagramm}} verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: <gallery> InheritancePgmUML.svg | Abgeleitete Klasse erbt von Basisklasse (Einfachvererbung) Diamond inheritance.svg | D erbt von B und C (Mehrfachvererbung). B und C erben von A (Einfachvererbung) </gallery> Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] ''Einschub:'' Ganz ähnlich kann man auch Klassen testen, z.B. in folgendem Code-Fragment class Fahrzeug: # siehe Abschnitt "Objektorientierte Programmierung" # ... if __name__ == "__main__": vauweh = Fahrzeug(170, 90) beemweh = Fahrzeug(200, 120) print(vauweh.convertGeschw()) print(beemweh.convertGeschw()) Wird das Script als Hauptprogramm ausgeführt (z.B. zu Testzwecken), so erfolgt die Ausgabe der zwei, via <code>convertGeschw()</code>, umgerechneten Geschwindigkeiten. Wird es aber als Modul eingebunden, so wird der letzte Programmabschnitt nicht ausgeführt (<code>__name__ == "__main__"</code> ergibt <code>False</code>). == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} qyvj009w5o870v41msr450cey5l45m6 1088136 1088135 2026-06-14T11:15:46Z Intruder 1513 /* if */ 1088136 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals, Beispiel 1): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Python-Code (formatted string literals, Beispiel 2): wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung sei aus anderen Programmiersprachen bereits bekannt. In {{W|Pseudocode}} lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE Als {{W|Aktivitätsdiagramm|UML-Aktivitätsdiagramm}} sieht das in etwa so aus: [[File:If-Then-Else-diagram.svg|200px]] Und als {{W|Nassi-Shneiderman-Diagramm|Nassi-Shneiderman-Struktogramm}} so: [[File:Zweiseitige Auswahl.png|250px]] In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Als Pseudocode sieht das so aus WENN bedingung TRUE führe block aus ENDE Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. In Pseudocode: WENN bedingung1 TRUE führe block1 aus SONST WENN bedingung2 TRUE führe block2 aus SONST WENN bedingung3 TRUE führe block3 aus SONST führe block4 aus ENDE Ein Python-Beispiel: a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Als Struktogramm sieht das in etwa so aus: [[File:Mehrseitige Auswahl.png|250px]] Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 === for === Struktogramm einer for-Schleife: [[File:Zählschleife.png|200px]] In Python bspw. so: for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = {{Wikipedia|Objektorientierte Programmierung}} * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odeling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das {{W|Klassendiagramm}} verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: <gallery> InheritancePgmUML.svg | Abgeleitete Klasse erbt von Basisklasse (Einfachvererbung) Diamond inheritance.svg | D erbt von B und C (Mehrfachvererbung). B und C erben von A (Einfachvererbung) </gallery> Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] ''Einschub:'' Ganz ähnlich kann man auch Klassen testen, z.B. in folgendem Code-Fragment class Fahrzeug: # siehe Abschnitt "Objektorientierte Programmierung" # ... if __name__ == "__main__": vauweh = Fahrzeug(170, 90) beemweh = Fahrzeug(200, 120) print(vauweh.convertGeschw()) print(beemweh.convertGeschw()) Wird das Script als Hauptprogramm ausgeführt (z.B. zu Testzwecken), so erfolgt die Ausgabe der zwei, via <code>convertGeschw()</code>, umgerechneten Geschwindigkeiten. Wird es aber als Modul eingebunden, so wird der letzte Programmabschnitt nicht ausgeführt (<code>__name__ == "__main__"</code> ergibt <code>False</code>). == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} 7hinh6myl4d2psw03jaos8829vmas00 1088137 1088136 2026-06-14T11:30:56Z Intruder 1513 /* while */ 1088137 wikitext text/x-wiki {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} = Hallo Welt und allgemeine Hinweise = == Was ist Python == * Python ist eine universelle höhere Programmiersprache. * Python ist objektorientiert. * Python ist Open-Source (Python Software Foundation License). * Python ist für viele Betriebssysteme erhältlich (z.B. für Linux, MS Windows, macOS). * Python ist ein Interpreter. * Python ist durch Module fast beliebig erweiterbar. * Python als Programmiersprache ist case-sensitive - d.h. Groß- und Kleinschreibung ist relevant bei der Eingabe von Befehlen. * Python ist in etlichen Anwendungsprogrammen (z.B. {{W|FreeCAD}}, {{W|LibreOffice}}, {{W|GIMP}}, {{W|Blender (Software) | Blender}}) als Makrosprache verwendbar. {{Wikipedia | Python (Programmiersprache)}} == Python installieren == === MS Windows === Laden Sie das aktuelle Python-Paket von der Webseite [https://www.python.org/] herunter. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm. Einfach das Installationsprogramm im Explorer doppelklicken und den Anweisungen des Setup-Programmes folgen. === Linux === Entweder ist Python bereits standardmäßig installiert, ansonsten ist die Installation mittels Paketmanagementsystem einfach möglich. Aber auch die Spyder-Entwicklungsumgebung ([https://www.spyder-ide.org]) bietet einen guten Einstieg mit Python (das gilt auch für MS Windows). Spyder bringt auch schon etliche wichtige Module standardmäßig mit. == Python starten == === MS Windows === Das Icon für das Python-Programm doppelklicken. Und schon startet das Programm. [[Datei:PythonIng_start1.jpg]] Python im interaktiven Modus präsentiert sich dann so: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Alternativ kann das Programm auch über die Eingabeaufforderung oder die PowerShell gestartet werden: c:\devel\Python>python.exe Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> === Linux === Tippen Sie einfach das Wort „python“ (oder unter openSUSE Tumbleweed z.B. auch „python3.11“ oder „python3.13“) in einem Linux-Terminal ein, schließen den Befehl mit der RETURN-Taste ab, und schon startet Python im interaktiven Modus: Python 3.13.12 (main, Feb 09 2026, 22:37:44) [GCC] on linux Type "help", "copyright", "credits" or "license" for more information. >>> Es gibt auch noch andere Möglichkeiten Python zwecks Programmausführung zu starten, z.&nbsp;B. den {{W|Shebang}} (<code>#!</code>) am Beginn eines Python-Scripts. Das Script sei als Script.py gespeichert. Dann kann das Script mit ./Script.py ausgeführt werden. Für openSUSE Tumbleweed sei nachfolgend ein lauffähiges "Hallo Welt!"-Script angegeben. Es wird in diesem Script der Python-Interpreter in der Version 3.13 verwendet : #!/usr/bin/python3.13 print("Hallo Welt!") Die Berechtigungen zum Ausführen der Datei müssen natürlich noch richtig gesetzt werden, z.B. mittels <code>chmod 777 Script.py</code>. <small><code>chmod</code> ist die Abkürzung für"'''ch'''ange file '''mod'''e bits".</small> <small>Die "Maske" <code>777</code> ist nur für Testzwecke sinnvoll, weil sie leicht zu merken ist und für alle Benutzer alle Zugriffsberechtigungen ('''r'''ead/'''w'''rite/e'''x'''ecute für owner/group/others) setzt. Im richtigen Einsatz wird man das aus Sicherheitsgründen nicht so handhaben, sondern nur die Berechtigungen setzen, die unbedingt erforderlich sind. Welche Zugriffsberechtigungen gesetzt sind, kann man z.B. mit dem Befehl <code>ls -l</code> oder <code>ll</code> ('''l'''i'''s'''t directory contents) erfragen. Aber dazu im Moment genug. Erfahrene Linux-Nutzer kennen das ohnehin und Anfänger sollen jetzt nicht mit Linux-Interna überfordert werden. Bei Bedarf siehe die Linux-Man-Pages oder dezidierte Bücher zu Linux.</code></small> <small>Oder das Script wird in einen Pfad verschoben, in dem sich ausführbare Programme generell befinden (<code>echo $PATH</code>). Das Script kann dann wie ein normales Programm ohne weitere Angaben mit Script.py gestartet werden. Alternativ wird nicht das Script an sich verschoben, sondern nur ein symbolischer Link angelegt, z.B. mit <code>ln -s ~/tmp/Script.py ~/.local/bin/Script.py</code>.<code>~/.local/bin</code> sei ein im PATH gelegenes Verzeichnis. Dies sind aber schon Features für fortgeschrittene Linux-Benutzer und werden am Anfang eher selten benötigt.</small> == Ein paar Worte zur Erklärung == Getestet wurden die Beispiele unter den Betriebssystemen * MS Windows 10 mit der Python-Version 3.12.0 (teilweise auch mit 3.12.2 und 3.13.1; nur die Inhalte die bis spätestens Juli 2025 erstellt wurden) * MS Windows 11 ab der Python-Version 3.13.4 (nur zum Teil; ab Juli 2025) * openSUSE Leap 15.6 mit der Python-Version 3.11.12 (Spyder, nur vereinzelt) und zum Teil mit 3.12.11 (ab Juli 2025 bis November 2025). * openSUSE Tumbleweed ab der Python-Version 3.13.9 (nur vereinzelt, ab November 2025) An Beliebtheit rangiert Python mit Stand März 2026 mit einem Rating von 21,25% an 1. Stelle vor C und C++ (lt. [https://www.tiobe.com/tiobe-index/ TPCI - TIOBE Programming Community Index]). Lt. [https://innovationgraph.github.com/global-metrics/programming-languages GitHub Top 50 Programming Languages Globally] lag Python im Q3/2025 auf Rang 2, vor TypeScript und hinter JavaScript. Der Name "Python" rührt von der Komikertruppe {{W|Monty Python}} her. Die Icons für Python (z.B. Python selbst, Eric IDE, IDLE) sind aber durch die Python-Schlangenart symbolisiert. <gallery> Python-logo-notext.svg|Python-Logo Guido van Rossum OSCON 2006.jpg|Guido van Rossum (geb. 1956), der Erfinder von Python </gallery> == Ein erstes Programm == Kommentare werden in Python mit der Raute (#) eingeleitet. Sie werden vom Python-Interpreter ignoriert. Text kann mit der print-Funktion ausgegeben werden. Starten Sie Python und geben sie folgende Anweisungen zeilenweise ein >>> # Das ist ein Kommentar >>> print("Hallo Welt!") Als Ergebnis erhalten Sie Hallo Welt! Der Prompt (>>>) ist selbstverständlich nicht einzutippen, sondern wird vom Python-System geliefert. Strings können in Python entweder in Anführungszeichen (") gesetzt werden oder in Hochkommatas('). In diesem Text wird die erste Variante bevorzugt eingesetzt. Im Gegensatz zu Julia ist es hier egal, ob zwischen <code>print</code> und der öffnenden Klammer Leerzeichen stehen. = Python als Taschenrechner = == Allgemeines == Wir wollen 3 * 5 berechnen. Dazu starten wir Python im interaktiven Modus. Geben Sie dann die Formel >>> 3 * 5 ein, drücken die Taste ENTER/RETURN ({{Taste|↵}}) und erhalten als Ergebnis 15 Auch kompliziertere Ausdrücke sind möglich. Beispielsweise mit Winkelfunktionen, Quadratwurzeln etc. Wir wollen nun den Ausdruck <math>\sin\sqrt{15}</math> berechnen : >>> import math >>> math.sin(math.sqrt(15)) -0.6679052983383519 Als erstes wird das math-Modul importiert. Dann wird der mathematische Ausdruck berechnet. Eine andere Variante, die dasselbe Ergebnis liefert, ist >>> from math import * >>> sin(sqrt(15)) -0.6679052983383519 Es wird also aus dem Modul <code>math</code> alles importiert (erkennbar am <code>*</code>). Will man nicht alles importieren, so kann man das auch einschränken: >>> from math import sin, sqrt Beenden lässt sich das Python-Programm durch Eingabe von <code>exit()</code> (und natürlich ist zur Bestätigung die RETURN-Taste zu drücken). == Die Hilfefunktion von Python == Bei Eingabe der Anweisung help() springt Python in den Hilfemodus. Eingabe: >>> help() Eingabe: help> math.sin Ausgabe: Help on built-in function sin in math: math.sin = sin(x, /) Return the sine of x (measured in radians). Für die komplette Python-Dokumentation siehe [https://docs.python.org/3/]. Verlassen kann man den Hilfemodus durch das Drücken von STRG-C. == Aufgaben == * Erkunden Sie die Tangensfunktion "tan" mittels Python-Hilfe (vergessen Sie nicht das math-Modul zu importieren und das <code>math.</code> vor <code>tan</code>) * Berechnen Sie mit Python den Ausdruck <math>\frac{1}{2}\cdot \text{e}^2 \cdot \tan(\pi/3)</math>. Siehe für die Exponentialfunktion im Python-Hilfesystem auch den Befehl <code>math.exp</code>. Alternativ kann auch die Konstante <code>math.e</code> eingesetzt werden. Potenzieren kann man bei Python mit dem **-Operator (z.B. 2**3 = 8). Für <math>\pi</math> gibt es <code>math.pi</code>. = Python als Scriptsprache = Häufig wird man aber kompliziertere Anweisungsfolgen verarbeiten müssen. Diese will man normalerweise nicht jedesmal neu eingeben, sondern in einer Datei speichern und diese Datei dann zur Ausführung bringen. Speichern Sie dazu folgenden Code in einer Textdatei, z.B. unter MS Windows als c:\tmp\test1.py # Das ist ein Kommentar print("Hallo Welt!") Python-Dateien werden mit der Dateiendung .py versehen. Achten Sie darauf, dass vor dem print keine Leerzeichen vorhanden sind. Das ist eine Python-Eigenheit. Wie wir später sehen werden, nutzt Python Einrückungen als syntaktisches Mittel, z.B. um bei Schleifen den Schleifenkörper zu kennzeichnen. Danach bringen Sie die Skriptdatei test1.py (sozusagen das Hauptprogramm) folgendermaßen zur Ausführung: 1) Starten Sie unter MS Windows die Eingabeaufforderung (oder alternativ auch die Windows PowerShell). Das sieht dann etwa so aus: Microsoft Windows [Version 10.0.19045.3693] (c) Microsoft Corporation. Alle Rechte vorbehalten. C:\Users\xyz> : <small>Falls jemand nicht weiß, wie man die Eingabeaufforderung startet: Eine Möglichkeit ist, einfach in der Taskleiste von Windows das "Start"-Symbol &nbsp;([[Image:Windows_logo_-_2021_(Black).svg|10px]])&nbsp; mit der rechten Maustaste anklicken. "Ausführen" auswählen (oder alternativ für die PowerShell unter Windows 10 den Eintrag "Windows PowerShell", unter Windows 11 den Eintrag "Terminal"). Im sich öffnenden Dialogfenster gibt man in die "Öffnen"-Zeile das Wort <code>cmd</code> ein und mit "OK" wird das Ganze bestätigt.</small> 2) Wechseln Sie mittels <code>cd c:\tmp</code> in das Verzeichnis c:\tmp 3) Angenommen, Sie haben Python unter dem Pfad <code>c:\devel\Python\</code> installiert. Starten Sie das Programm so (der Prompt <code>c:\tmp></code>ist natürlich nicht mit einzutippen): c:\tmp>c:\devel\Python\python.exe test1.py 4) Wie erwartet ergibt sich folgende Ausgabe am Bildschirm Hallo Welt! Die Vorgehensweise unter Linux ist prinzipiell gleich. Die kleinen Unterschiede, wie z.B. der Slash statt dem Backslash in Pfadangaben, sollten für Linux-Benutzer keine Hürde darstellen. == Variablen == Variablenbezeichner können aus Buchstaben (A-Za-z), Ziffern (0-9) und Underscores (_) bestehen, dürfen aber nicht mit einer Zahl beginnen. Führende Underscores haben u.a. im Kontext mit der Objektorientierten Programmierung eine spezielle Bedeutung und sollten nicht für "normale" Variablenbezeichner verwendet werden. Gültige Variablenbezeichner wären also: xyz x1 _wert name_anzahl Es gibt in Python etliche Schlüsselwörter (z.B. for, if oder return). Diese dürfen nicht als eigene Variablenbezeichner verwendet werden. Eine Liste aller Schlüsselwörter liefert das Script import keyword print(keyword.kwlist) <small>Übung: Speichern Sie dieses Script in eine Datei, z.B. in c:\tmp\test1.py. Führen Sie diese Datei aus, um die Liste der Schlüsselwörter auszugeben.</small> Da Python case-sensitiv ist, repräsentieren folgende Bezeichner verschiedene Variablen: xyz XYZ xYz Werte werden an Variablen mittels Gleich-Zeichen (=) zugewiesen. Im Folgenden wird der Code immer in der Datei c:\tmp\test1.py gespeichert. x = 5 y = 10 z = x*y print(z) Bringen Sie die Datei test1.py zur Ausführung so erhalten Sie folgende Bildschirmausgabe 50 Sie können auch mehrere Anweisungen in einer Zeile durch Semikolon getrennt schreiben. Dies führt aber zu unübersichtlichem Code. x = 5; y = 10; z = x*y; print(z) Ausgabe: 50 Auch aus der Programmiersprache C/C++ oder Java bekannte Konstrukte können Sie verwenden, z.B. x = 5 # x = x - 2 x -= 2 print(x) Bildschirmausgabe: 3 Beachten Sie, dass mit dem =-Zeichen eine Wertezuweisung durchgeführt wird. Dies ist nicht äquivalent zum mathematischen =-Zeichen, wie am vorigen Beispiel zu ersehen ist. Den Inkrement-/Dekrementoperator (z.B. x++ oder x--) aus C/C++ oder Java kennt Python aber nicht. Variablen sind nicht an einen bestimmten Datentyp gebunden, folgendes ist mit Python problemlos möglich: import math wert = 10 print(wert) wert = 35.5 print(wert) wert = "Hallo" print(wert) wert = math.pi print(wert) Ausgabe: 10 35.5 Hallo 3.141592653589793 == Physische und logische Zeilen == In Python muss eine Anweisung in einer logischen Zeile Platz finden. Wird eine Anweisung aber zu lang für eine Zeile, dann kann sie in mehrere physische Zeilen unterteilt werden. Dies kann einerseits durch einen Backslash am Ende einer Zeile geschehen, z.B. a = 2 + \ 5 Dies stellt eine logische Zeile dar, die in zwei physische Zeilen unterbrochen ist. Geklammerte Ausdrücke werden automatisch zu einer logischen Zeile verbunden, z.B. a = (2 + 5) Achtung: Im ersten Beispiel darf nach dem Backslash nichts mehr stehen, auch kein Kommentar. Dies trifft im zweiten Bespiel nicht zu, hier könnte noch ein Kommentar folgen, z.B. a = (2 + # Kommentar 5) Auch für Strings gibt es Möglichkeiten, diese auf mehrere Zeilen aufzuspalten. # Kurzer String str1 = "ABC" # Langer String str2 = """Hallo Welt, Grüetzi Schwyzer, Servus an alle""" # Backslash str3 = "UVW\ XYZ" # Mit Klammern str4 = ("Sehr langer Text, der automatisch .............. " "in einer einzigen Variable zusammengefügt wird." ) print(str1) print(str2) print(str3) print(str4) Ausgabe: ABC Hallo Welt, Grüetzi Schwyzer, Servus an alle UVWXYZ Sehr langer Text, der automatisch .............. in einer einzigen Variable zusammengefügt wird. ==Hexadezimale, oktale, binäre und andere Zahlen== d = 1050 # Dezimalzahl h = 0xAA2 # Hexadezimalzahl o = 0o12 # Oktalzahl b = 0b100001101 # Binärzahl print(d) print(h) print(o) print(b) Ausgabe: 1050 2722 10 269 Groß- und Kleinbuchstaben sind in obigen Literalen übrigens egal. So kann man z.B. statt <code>0b1001</code> auch <code>0B1001</code> schreiben (siehe dazu [https://docs.python.org/3/reference/lexical_analysis.html#integer-literals]). Sie können auch dezimale in hexadezimale Zahlen umwandeln, usw.: h = hex(1050) # Dezimalzahl -> Hexadezimalzahl b = bin(1050) # Dezimalzahl -> Binärzahl o = oct(1050) # Dezimalzahl -> Oktalzahl print(h) print(b) print(o) Ausgabe: 0x41a 0b10000011010 0o2032 Gegeben sei die Zahl 121 zur Basis 3. Diese soll in eine Dezimalzahl umgewandelt werden. Das kann so geschehen: z = int("121", 3) print(z) Ausgabe: 16 Dass dies richtig ist, davon kann man sich folgendermaßen überzeugen: <math> 1 \cdot 3^2 + 2 \cdot 3^1 + 1 \cdot 3^0 = 9 + 6+ 1 = 16 </math> Zahlen übersichtlicher schreiben kann man auch mittels Underscore, z.B.: print("Eine Million (Variante 1) =", 1000000) print("Eine Million (Variante 2) =", 1_000_000) print("Eine Rechnung:", 2_000 * 400_000); Es ergibt sich bei beiden Varianten die gleiche Ausgabe. Variante 2 ist aber im Sourcecode leichter lesbar, detto die Zahlen in der Rechnung: Eine Million (Variante 1) = 1000000 Eine Million (Variante 2) = 1000000 Eine Rechnung: 800000000 == Strings und Platzhalter== Ein paar einfache Beispiele: print("Hallo {}" . format("Hugo")) print("Hallo {:s}" . format("Hugo")) print("Hallo %s" % "Hugo") Ausgabe: Hallo Hugo Hallo Hugo Hallo Hugo Python-Code (formatted string literals, Beispiel 1): str1 = "Hallo" str2 = "Hugo" print(f"{str1} {str2}") Ausgabe: Hallo Hugo Python-Code (formatted string literals, Beispiel 2): wert = 11.567 print(f"Ausgabe: {wert:.5f}") Ausgabe: Ausgabe: 11.56700 Komplexere Beispiele: print("Hallo {} und {}" . format("Hugo", "Mike")) print("Hallo {name1} und {name2}" . format(name2="Hugo", name1="Mike")) # Füllzeichen: * # Bündigkeit: > (=rechts), < (=links), ^ (=zentriert) # Feldweite: 10 # Typ: s (=String), f (=Gleitkommazahl), d (=Dezimalzahl) etc. print("Hallo {:*>10s}" . format("Hugo")) print("Hallo {:*<10s}" . format("Hugo")) Ausgabe: Hallo Hugo und Mike Hallo Mike und Hugo Hallo ******Hugo Hallo Hugo****** Python-Code: str = "Hallo\t%s\t%7.2f\t%10.2e\t%i" % ("Hugo", 12.34567, 34.567, 264) print(str) Ausgabe: Hallo Hugo 12.35 3.46e+01 264 == Unicode == Neben den bekannten ASCII-Zeichen lassen sich Zeichen auch mittels Unicode beschreiben. Griechische Buchstaben oder komplexere mathematische Operatoren - all das sollte kein Problem sein. Siehe auch {{W|Unicode}}, {{W|Liste der Unicodeblöcke}} und {{W|Unicodeblock Mathematische Operatoren}}. Im Folgenden werden ein paar Zeichen (Allquantor, Nabla-Operator, Existenzquantor), die man aus der Mathematik kennt, erzeugt. ch1 = "\N{FOR ALL}" ch2 = "\N{NABLA}" ch3 = "\u2203" print(ch1, ch2, ch3) Ausgabe: ∀ ∇ ∃ <small>Diese Ausgabe ergibt sich z.B. mit der IDLE-Shell oder mit Cygwin. Beim Ausführen über die Windows-Eingabeaufforderung oder Windows PowerShell unter MS Windows 10 erfolgt keine korrekte Darstellung. IDLE ist die mit Python mitgelieferte IDE ('''I'''ntegrated '''D'''evelopment '''E'''nvironment, Integrierte Entwicklungsumgebung). Gegen Ende dieses Textes wird IDLE kurz beschrieben. Das Problem mit der Windows Eingabeaufforderung lässt sich aber umgehen. Man muss nur eine Schriftart auswählen, die die Zeichen kennt, z.B. "DejaVu Sans Mono". Dazu klicken Sie einfach bei der Eingabeaufforderung mit der rechten Maustaste oben auf die weiße Leiste und wählen im aufpoppenden Fenster den Menüpunkt "Eigenschaften". Es öffnet sich ein Dialogfenster. Über den Reiter "Schriftart" lässt sich nun die Schriftart einstellen. Unter MS Windows 11 oder openSUSE Leap 15.6 (bash-Konsole) gibt es dieses Problem ohnehin nicht.</small> == Reguläre Ausdrücke == Python kennt auch {{W|Regulärer Ausdruck|reguläre Ausdrücke}}. Dazu gibt es in Python das Modul <code>re</code>. Beipielsweise sollen alle Zahlen (<math>\text{zahl}\in\mathbb{N}_0</math>) in einem String gesucht und ausgegeben werden. Als String sei gegeben: <code>3x Grüße und 100 Kekse.</code> Das Muster (Pattern) ist <code>\d+</code>. <code>\d</code> steht für eine Dezimalziffer 0-9. Das Plus-Zeichen (+) steht symbolisch für ein oder mehrere Zeichen des vorherigen Ausdrucks. Hier also ein oder mehrere Dezimalziffern. Es wird die Funktion <code>findall</code> aus dem Modul <code>re</code>verwendet. Python-Code: from re import findall str = "3x Grüße und 100 Kekse." pat = "\\d+" # Doppel-Backslashes müssen verwendet werden, sonst gibt Python eine Warnung aus! # alternativ: pat = r"\d+" # oder: pat = "[0-9]+" numb = findall(pat, str) print(numb) Ausgabe: ['3', '100'] Python kennt noch viele weitere Möglichkeiten mittels regulärer Ausdrücke zu hantieren. Dies soll hier aber nicht vertieft werden, da das Thema schon ziemlich speziell und komplex ist. Bei Bedarf siehe aber z.B. die Bücher ''Weigend, Seite 380ff'' und ''Ernesti, Kaiser'' [https://openbook.rheinwerk-verlag.de/python/28_001.html] oder die Python-Dokumentation [https://docs.python.org/3/library/re.html]. Auch [[Python unter Linux: Reguläre Ausdrücke]] liefert ein umfangreiches und brauchbares Python-2-Kapitel zu den regulären Ausdrücken. Die dort gelisteten Beispiele müssten ggf. vor Verwendung auf Python-3 umgeschrieben werden. <small>Wie macht man das? Dazu siehe z.B. [https://openbook.rheinwerk-verlag.de/python/43_001.html], [https://portingguide.readthedocs.io/en/latest/] oder [https://www.digitalocean.com/community/tutorials/how-to-port-python-2-code-to-python-3]</small> <small>Es gibt auch ein externes Modul ''regex'', das bei Bedarf extra installiert werden muss ([https://pypi.org/project/regex/]). Es bietet zusätzliche Funktionalität und gründlicheren Unicode-Support. Dies sei hier aber nur der Vollständigkeit halber erwähnt.</small> == Verzweigungen == === if === Die IF-Verzweigung sei aus anderen Programmiersprachen bereits bekannt. In {{W|Pseudocode}} lässt sie sich folgendermaßen darstellen: WENN bedingung TRUE führe block1 aus SONST führe block2 aus ENDE Als {{W|Aktivitätsdiagramm|UML-Aktivitätsdiagramm}} sieht das in etwa so aus: [[File:If-Then-Else-diagram.svg|200px]] Und als {{W|Nassi-Shneiderman-Diagramm|Nassi-Shneiderman-Struktogramm}} so: [[File:Zweiseitige Auswahl.png|250px]] In Python gibt es keinen expliziten ENDE-Kennzeichner. Stattdessen wird der Code durch Einrückungen strukturiert. Alles mit der gleichen Einrückungstiefe gehört zum selben Block. Dies zeichnet Python vor anderen Programmiersprachen aus. Die test1.py-Datei laute also wie folgt: x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: Der else-Zweig wird ausgefuehrt x ist groesser oder gleich 4 Man achte auch auf die Doppelpunkte in der if- und else-Zeile. Darauf vergisst man gerne, wenn man von anderen Programmiersprachen kommt. Folgendes wäre in Python ein Fehler (genauer gesagt ein IndentationError). x = 5 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Auch Nachstehendes würde nicht zum gewünschten Ergebnis führen (löst aber keine Fehlermeldung aus). Der letzte print-Befehl ist schon außerhalb der IF-ELSE-Verzweigung. x = 3 if x < 4: print("x ist kleiner als 4") else: print("Der else-Zweig wird ausgefuehrt") print("x ist groesser oder gleich 4") Ausgabe: x ist kleiner als 4 x ist groesser oder gleich 4 Python kennt eine Reihe von Vergleichs- und Verknüpfungsoperatoren: <, <= ... kleiner (gleich) >, >= ... größer (gleich) == ... gleich != ... ungleich is ... identisch is not ... nicht identisch and ... AND or ... OR not ... NOT Beispielsweise: a = 5 b = 9 if a<=10 and b!=7: print("OK") else print("Nicht OK") Ausgabe: OK Der else-Block kann übrigens auch ersatzlos entfallen. Als Pseudocode sieht das so aus WENN bedingung TRUE führe block aus ENDE Mehrfache Verzweigungen werden durch das elif-Konstrukt erstellt. In Pseudocode: WENN bedingung1 TRUE führe block1 aus SONST WENN bedingung2 TRUE führe block2 aus SONST WENN bedingung3 TRUE führe block3 aus SONST führe block4 aus ENDE Ein Python-Beispiel: a = 14 if a<=10: print("<=5") elif a>11 and a<15: print("11 bis 15") elif a>16 and a<20: print("16 bis 20") else: print(">=20") Ausgabe: 11 bis 15 In Python gibt es auch die Schlüsselwörter <code>True</code> (für wahr) und <code>False</code> (für falsch). Man beachte, dass sie mit Großbuchstaben beginnen. Andere Schreibweisen wären ein Fehler. Sie gehören zum Datentyp <code>bool</code>. Ihnen sind auch die Zahlen <code>1</code> und <code>0</code> zugewiesen. === match === Ab Python 3.10 gibt es auch die match-Anweisung. Dies ist das Python-Pendant für die switch-Anweisung in anderen Programmiersprachen, geht aber bei näherer Betrachtung weit darüber hinaus. Hier nur ein einfaches Beispiel: x = "Hello" match x: case "Servus" | "Ciao": # or print("Servus an alle") case "Grüetzi": print("Grüetzi Schwyzer") case _: # other, default, sonstiges ... print("Hallo Welt") Ausgabe: Hallo Welt Als Struktogramm sieht das in etwa so aus: [[File:Mehrseitige Auswahl.png|250px]] Für nähere Details siehe z.B. [https://www.geeksforgeeks.org/python-match-case-statement/], [https://learnpython.com/blog/python-match-case-statement/], [https://docs.python.org/3/tutorial/controlflow.html#match-statements] und das Python Enhancement Proposal (PEP) 636 – Structural Pattern Matching: Tutorial [https://peps.python.org/pep-0636] und dort insbesondere den Anhang A - Quick Intro. <small><code>match, case, _</code> etc. sind sogenannte ''soft keywords''. Im Gegensatz zu den normalen Schlüsselwörtern dürfen ihnen auch Werte zugewiesen werden. Eine Liste der weichen Schlüsselwörter lässt sich durch <code>keyword.softkwlist</code> erstellen (die Anweisung gibt es seit Python 3.9). Siehe dazu auch [https://stackoverflow.com/questions/65800344/what-are-soft-keywords] und [https://docs.python.org/3/library/keyword.html#keyword.softkwlist].</small> == Schleifen == === while === Die WHILE-Schleife ist kopfgesteuert. Sie funktioniert wie aus anderen Programmiersprachen bekannt. In Pseudocode: SOLANGE bedingung TRUE führe block aus ENDE In Python: x = 0 while x <= 10: print(x) x += 1 Ausgabe: 0 1 2 3 4 5 6 7 8 9 10 Eine Endlosschleife ergibt sich z.B. so: i = 5 while True: print(i) i-=1 Beendet werden kann sie mit der Tastenkombination STRG-C. Eine fußgesteuerte Schleife kann so simuliert werden: i = 5 while True: print(i) i-=1 if i<=0: print("Fire") break Ausgabe: 5 4 3 2 1 Fire === for === Struktogramm einer for-Schleife: [[File:Zählschleife.png|200px]] In Python bspw. so: for x in range(6): print(x*2) Ausgabe: 0 2 4 6 8 10 Die Schleife läuft von 0 bis 5. Ausgegeben wird jeweils der Wert x*2. Aquivalent kann diese Schleife auch so geschrieben werden: for x in range(0, 11, 2): print(x) Die Ausgabe ist wie oben. Der Startwert sei 0, der Endwert ist 11-1 und die Schrittweite ist 2. Ein anderes Beispiel sei for x in "text": print(x) Ausgabe: t e x t == Schleifen abbrechen == === break === <code>break</code> bricht die Schleife ab und setzt mit dem nächsten Befehl außerhalb der Schleife fort. for var in range(100): print(var) if var == 5: break Ausgabe: 0 1 2 3 4 5 === continue === <code>continue</code> bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Schleifendurchlauf fort. for var in range (11): if var == 5: continue print(var) Ausgabe: 0 1 2 3 4 6 7 8 9 10 == try - except == try: z1 = 12 / 0 print(z1) except ZeroDivisionError: print("Das Ergebnis ist unendlich") except: print("Kann nicht berechnet werden!") print("Bitte die Formel korrigieren!") Ausgabe: Das Ergebnis ist unendlich Es wird versucht, eine Zahl durch Null zu dividieren. Das ist nicht möglich, es wird eine Ausnahme ausgelöst. Das Programm springt daher in den except-ZeroDivisionError-Block und führt die dort gelisteten Anweisungen aus (in unserem Fall eine print-Anweisung). Würden wir dieses Programm ohne try-except ausführen, so ergibt sich aus z1 = 12 / 0 print(z1) folgende Fehlermeldung und ein unmittelbarer Programmabbruch Traceback (most recent call last): File "C:\tmp\test1.py", line 1, in <module> z1 = 12 / 0 ZeroDivisionError: division by zero Mit dem try-except-Mechanismus können also Ausnahmen oder Fehler aufgefangen und behandelt werden. In unserem Beispiel ist das eher trivial, aber bei größeren Programmen kann das durchaus Sinn machen. == pass == Ein leerer Block muss in Python mittels dem Schlüsselwort <code>pass</code> dargestellt werden. Z.B. x = 2 if x == 1: print("Wert ist ", x) else: pass Würde man das <code>pass</code> im else-Block weglassen, so würde man eine Fehlermeldung erhalten: IndentationError: expected an indented block after 'else' statement on line 5 = Funktionen = == Aufrufen von Funktionen == Funktionen sind uns im Rahmen dieses Kurses schon zuhauf begegnet. Sei es die print()-, die math.sin()- oder die hex()-Funktion. All diese Funktionen werden von Python zur Verfügung gestellt, ohne dass man sie explizit programmieren müsste. Aufgerufen werden diese Funktionen, indem man ihren Namen eintippt, gefolgt von runden Klammern. In diesen Klammern können noch Argumente übergeben werden. Auch Rückgabewerte sind möglich. == Funktionen selber schreiben == Funktionen werden mit dem def-Schlüsselwort (man definiert die Funktion) eingeleitet, danach folgt der Funktionsname, danach wiederum runde Klammern, in denen formale Argumente stehen können. Abgeschlossen wird die def-Zeile mit einem Doppelpunkt. Danach folgt der Funktionskörper. Dieser Funktionskörper muss wiederum eingerückt werden (wie von den Verzweigungen und Schleifen bekannt). Aufgerufen wird diese Funktion, indem man ihren Funktionsnamen eingibt, gefolgt von runden Klammern (ggf. mit den aktuellen Parametern). Z.B. # Funktion definieren def halloWelt(i): # i ... beliebige Ganzzahl print("Hallo " * i, end="") print("Welt!") # Funktion aufrufen halloWelt(3) Ausgabe: Hallo Hallo Hallo Welt! Unterschied zwischen formalen und aktuellen Parametern: [[Datei:PythonIng_func1.jpg]] <small>Aktuelle Parameter werden auch Argumente genannt.</small> Rückgabe von Funktionswerten: # Funktion definieren def mathFunc(a, b): r1 = a + b r2 = a * b return r1, r2 # Funktion aufrufen a, b = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(a) print(b) Ausgabe: 8 15 Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 a, b = mathFunc(3, 5) print(a) print(b) a, b = mathFunc(5) print(a) print(b) a, b = mathFunc(b=6) print(a) print(b) Ausgabe: 8 15 25 100 16 60 == Lambda-Funktionen == print((lambda a, b: a*b) (3, 5)) Ausgabe: 15 Eingeleitet wird eine Lambda-Funktion (auch Lambda-Form, Lambda-Operator oder anonyme Funktion genannt) mit dem Schlüsselwort <code>lambda</code>. Es folgen die formalen Argumente, danach ein Doppelpunkt, die Berechnungsvorschrift und ggf. abschliessend in Klammern die aktuellen Parameter. Man kann einer Lambda-Funktion auch einen Funktionsnamen geben und die Funktion über diesen Namen aufrufen, z.B. prod = lambda a, b: a*b print(prod(3, 5)) Als Ausgabe wird wieder die Zahl 15 geliefert. == Rekursive Funktionen == Funktionen können wiederum andere Funktionen aufrufen. Von einem rekursiven Funktionsaufruf spricht man, wenn die aufgerufene Funktion gleich der aufrufenden ist. def printFunc(i): if (i >= 5): return else: print("Hallo Welt") printFunc(i+1) printFunc(1) Ausgabe: Hallo Welt Hallo Welt Hallo Welt Hallo Welt == Funktionsannotationen == Python ist sehr flexibel, was Typen angeht. Im Vorhergehenden haben wir generell keine Typangaben gemacht. Will man Typen angeben, so bietet Python das Konzept der Funktionsannotation. def calcFunc(a: int, b: int) -> int: return a+b r1 = calcFunc(8, 9) r2 = calcFunc(8.0, 9.0) r3 = calcFunc("Hallo", "Welt") print(r1) print(r2) print(r3) Ausgabe: 17 17.0 HalloWelt Jetzt sieht man auf den ersten Blick, welche Typen der Programmierer im Sinn hatte, als er die Funktion erstellte. Das Problem dabei ist nur, dass es Python ziemlich egal ist, welche Typen man im Endeffekt eingibt. Im obigen Beispiel können statt Integer-Typen u.a. auch Float- oder String-Typen eingegeben werden. <small> Siehe zum Thema "Type Checking" aber auch den später folgenden Abschnitt [[Ing_Mathematik:_Python#Type_Checker]]. </small> == Variadische Funktionen == Python-Code: def test1(a, *b): print(a); for c in b: print(c); test1("Hallo", "Welt", "Schweizer", "und alle anderen") Ausgabe: Hallo Welt Schweizer und alle anderen Mit dem Stern (auch als Splat-Operator bezeichnet) in der formalen Parameterliste bei der Funktion <code>test1</code> wird angezeigt, dass eine beliebige Anzahl von Argumenten übergeben wird. <small> Dies entspricht in etwa dem, was in anderen Programmiersprachen (PHP etc.) mittels Ellipse (<code>...</code>) angezeigt wird.</small> = Tupel, Listen und andere = [[Datei:Python 3. The standard type hierarchy.png|mini|hochkant=1.7|Datentypen und Strukturen]] Tupel, Listen und einige andere sind Datenstrukturen oder Sequenzen. Listen (z.B. eine Einkaufsliste) sind veränderbar (mutable). Ein Tupel kann dagegen nicht verändert werden (immutable). Listen werden beim Anlegen in eckige Klammern eingeschlossen, Tupel in runde Klammern. Beim Tupel können die Klammern auch weggelassen werden. Ein Tupel mit nur einem Element muss mit einem Beistrich abgeschlossen werden. Der Grund ist, dass Python sonst nicht entscheiden kann, ob ein Tupel angelegt werden soll, oder nur ein geklammerter Wert. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. Als Gedächtnisstütze kann man sich den Unterschied zwischen Tupel und Liste ev. so leichter merken: : T'''u'''pel ... r'''u'''nde Klammern, '''u'''nveränderlich : L'''i'''ste ... eck'''i'''ge Klammern, veränderl'''i'''ch. # Liste und Tupel liste = [1, 2, "Hallo"] tupel = (1, 2, "Hallo") # Ausgabe von liste und tupel print(liste) print(tupel) # Ausgabe von Einzelelementen print(liste[1]) print(tupel[2]) # Element an Liste anhängen und einfügen liste.append(55) liste.insert(4, "Welt") print(liste) # Element aus Liste entfernen liste.remove(1) print(liste) # einige weitere Beispiele liste2 = [1,] tupel2 = 1, 2 tupel3 = (1,) print(liste2) print(tupel2) print(tupel3) Ausgabe: [1, 2, 'Hallo'] (1, 2, 'Hallo') 2 Hallo [1, 2, 'Hallo', 55, 'Welt'] [2, 'Hallo', 55, 'Welt'] [1] (1, 2) (1,) Beispiel: woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt Zu den Datenstrukturen gehören weiters auch Mengen und Dictionaries. Mengen sind von der Mathematik bekannt, sie sind ungeordnet und es kommen keine mehrfachen Elemente vor. Dictionaries sind durch Schlüssel :Wert-Paare gekennzeichnet. Mengen werden beim Anlegen wie Dictionaries in geschweifte Klammern eingeschlossen. dict = {"vorname":"Hugo", "nachname":"Meister" } menge = {1, 1, 3, 4, 4, 4, "Hallo"} print(dict) print(menge) print(dict["vorname"]) Ausgabe: {'vorname': 'Hugo', 'nachname': 'Meister'} {1, 3, 4, 'Hallo'} Hugo Geschweifte Klammern ohne Inhalt stellen Dictionaries dar und keine Mengen: di = {} print(type(di)) Ausgabe: <class 'dict'> == List Comprehensions == Aus einer Eingabeliste soll eine Ausgabeliste erzeugt werden. Das kann folgendermaßen geschehen. Mathematische Schreibweise: <math>lc = \{2x|x\in\ \mathbb{N}, 1\le x < 11\}</math> Python-Code: lc = [x*2 for x in range(1,11)] print(lc) Ausgabe: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Mathematische Schreibweise: <math>lc = \{2x | x \in \mathbb{N}, 1\le x < 11, x \bmod 2 = 0 \}</math> Python-Code: lc = [x*2 for x in range(1,11) if x%2 == 0] print(lc) Ausgabe: [4, 8, 12, 16, 20] Siehe auch {{W|List Comprehension}}. == Set Comprehensions == Dies ist sehr ähnlich wie im vorigen Abschnitt beschrieben. Es wird aber keine Liste, sondern eine Menge erzeugt. sc = {x*2 for x in range(1,11)} print(sc) Ausgabe: {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} == Listen zusammenführen - zip() == li1 = ["A", "B", "C", "D"] li2 = [1, 2, 3, 4] li3 = [5.5, 6.6, 7.7, 8.8] z = zip(li1, li2, li3) print(z) li4 = list(z) print(li4) Ausgabe: <zip object at 0x00000283B6C6AC80> [('A', 1, 5.5), ('B', 2, 6.6), ('C', 3, 7.7), ('D', 4, 8.8)] == Generatorausdruck == g = (i*2 for i in range(1,11)) print(g) t = tuple(g) print(t) print(t[1:3]) Ausgabe: <generator object <genexpr> at 0x00000241D2A4A5A0> (2, 4, 6, 8, 10, 12, 14, 16, 18, 20) (4, 6) == Slicing == slice ... Scheibe, Teil, in Scheiben schneiden Beispiel: Zugriff auf Elemente eines geordneten sequentiellen Objekttyps (Liste, Tupel oder String): str1 = "Hallo" # Das erste Element wird mit dem Index 0 angesprochen # [start (inkl.) : stop (exkl.) : step (default=1)] str2 = str1[0:2] # Alternativ auch: str2 = str1[:2] print(str2) tup1 = (0,1,2,3) # Das letzte Element hat auch den Index -1, das vorletzte den Index -2 usw. tup2 = tup1[-3:-1] print(tup2) lst1 = [[1, 5, 10, 20], [30, 40, 50, 60]] lst2 = lst1[1][1] print(lst2) Ausgabe: Ha (1, 2) 40 Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH = Objektorientierte Programmierung = {{Wikipedia|Objektorientierte Programmierung}} * {{W|Klasse (Objektorientierung)|Klasse}} ... die Schablone oder der Bauplan, enthält Methoden und Attribute * {{W|Objekt (Programmierung)|Objekt}} ... eine Klasseninstanz (die konkrete Ausprägung der Klasse) * {{W|Attribut (Programmierung)|Attribute}} ... die Eigenschaften eines Objekts * {{W|Methode (Programmierung)|Methoden}} ... die Aktionen (Operationen), die ein Objekt ausführen kann * {{W|Vererbung (Programmierung)|Vererbung}} ... neue Klassen (Subklassen, Unterklassen, abgeleitete Klassen) aus vorhandenen Klassen (Superklassen, Oberklassen, Basisklassen, Elternklassen) ableiten. Ermöglicht den Aufbau von Klassenhierarchien. Die Subklasse "erbt" Attribute und Methoden von der Superklasse. Python unterstützt (so wie C++, aber im Gegensatz zu Java) Mehrfachvererbung. == UML == * {{W|Unified Modeling Language|UML}} ... '''U'''nified '''M'''odeling '''L'''anguage, eine Modellierungssprache. Die UML enthält zahlreiche Diagrammarten, um Programme zu modellieren. Im Nachfolgenden wird nur das {{W|Klassendiagramm}} verwendet. [[File:UmlCd Klasse-3.svg|300px]] * {{W|Sichtbarkeit (Programmierung)|Sichtbarkeit}}: ** + ... public (öffentlich) ** - ... private ** # ... protected * {{W|Attribut_(UML)#Attribute_für_Instanzen_und_für_Klassen|Klassenattribute}} (statische Attribute): Werden nur einmal pro Klasse angelegt. Im Klassendiagramm werden sie unterstrichen. [[File:Attribute-3.png|200px]] * Vererbung: <gallery> InheritancePgmUML.svg | Abgeleitete Klasse erbt von Basisklasse (Einfachvererbung) Diamond inheritance.svg | D erbt von B und C (Mehrfachvererbung). B und C erben von A (Einfachvererbung) </gallery> Für weitergehende Betrachtungen zur UML wird auf Spezialliteratur verwiesen, z.B.: * Seidl et al.: UML@Classroom. dpunkt, 2012, ISBN 978-3-89864-776-2 * Rupp et al.: UML 2 glasklar. Hanser, 4. Aufl., 2012, ISBN 978-3-446-43057-0 == Eine einfache Klasse == [[Datei:PythonIng_uml1.svg | 200px]] class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 fahr = Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die Klasse Fahrzeug wird durch das class-Schlüsselwort eingeleitet. raeder ist ein Klassenattribut und public. __init__ wird bei der Objekterzeugung automatisch aufgerufen. Man achte darauf, dass diese Methode immer mit zwei Unterstrichen eingeleitet und abgeschlossen wird. Instanzattributen wird das Wort self vorangestellt. Wir sehen uns z.B. das Attribut self.__geschwind an. Auch hier werden zwei Unterstriche verwendet. Das bedeutet, dass dieses Attribut private ist. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. == Klassen importieren == Häufig ist es sinnvoll und übersichtlicher Klassen in eigenen Dateien zu speichern. Das sind dann eigene Module. Abgespeichert werden Sie mit der Endung py, wie bisher auch praktiziert. Aufgerufen werden Sie mit der import-Anweisung. Dann ist aber nur der Dateiname ohne Endung py zu verwenden. Klarer wird das mit einem Beispiel. Datei c:\tmp\fahrzeug.py class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 Datei c:\tmp\test1.py import fahrzeug fahr = fahrzeug.Fahrzeug(150, 90) print(fahr.convertGeschw()) Ausgabe: 41.666666666666664 Die üblichen import-Anweisungen lauten wie folgt: {| {{prettytable}} ! import-Befehl ! Instanz |- | import xyz || xyz.Klasse |- | import xyz as x || x.Klasse |- | from xyz import Klasse || Klasse |- | from xyz import * || Klasse |} Der Vorteil der ersten beiden import-Anweisungen ist, dass es kaum zu Namenskollisionen kommen kann. Dafür hat man bei den letzten beiden Varianten weniger Tipparbeit. == Vererbung == [[Datei:PythonIng_uml2.svg | 200px]] Datei fahrzeug.py: class Fahrzeug: raeder = 4 def __init__(self, geschwindigkeit, leistung): self.__geschwind = geschwindigkeit self.__leistung = leistung def setGeschwindigkeit(self, geschwindigkeit): # geschwindigkeit in km/h self.__geschwind = geschwindigkeit def setLeistung(self, leistung): self.__leistung = leistung def convertGeschw(self): # geschwindigkeit in m/s rueckgeben return self.__geschwind / 3.6 class Luftfahrzeug(Fahrzeug): def __init__(self, geschwindigkeit, leistung, fluegel): super().__init__(geschwindigkeit, leistung) self.__flueg = fluegel def getFlueg(self): return self.__flueg Datei test1.py: import fahrzeug fahr = fahrzeug.Luftfahrzeug(150, 90, 4) print(fahr.getFlueg()) Ausgabe: 4 = Grafiken zeichnen = Für das Zeichnen von Grafiken wird hier das Modul <code>matplotlib</code> verwendet. <code>matplotlib</code> ist ein externes Modul und muss vor der ersten Verwendung installiert werden. Das geht so: # Starten Sie ein Terminal (bei Windows die Eingabeaufforderung). # Führen Sie darin folgenden Befehl aus <code>c:\devel\Python\Scripts\pip.exe install matplotlib</code> pip ist übrigens der Paketmanager von Python ({{W|Pip_(Python)}}). Optimalerweise installieren wir auch gleich das Modul <code>numpy</code> (Numerical Python). Wir werden es im Folgenden oft benötigen (nicht nur bei den Grafiken). Das funktioniert vom Prinzip her genauso, wie für <code>matplotlib</code> gezeigt. <small>Verwenden Sie Spyder, so sind diese Schritte nicht nötig. Spyder inkludiert diese Pakete standardmäßig. Unter openSUSE Tumbleweed lassen sich diese Pakete mittels YaST oder zypper installieren.</small> == 2D == === Graph einer Funktion === Es soll die cosh-Funktion im Intervall <math>x\in[-3,3]</math> gezeichnet werden. Der Programmcode lautet in der einfachsten Form: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh1.jpg]] Der Code ist quasi selbsterklärend. Das Untermodul pyplot des matplotlib-Moduls und das numpy-Modul werden importiert. x läuft von -3 bis +3. y wird für jeden x-Wert per Formel ausgerechnet. "plt.plot()" ist der Zeichenbefehl. "plt.show" ist notwendig, um das Fenster mit der Grafik anzuzeigen. Die Schrittweite 0.1 wurde so gewählt, um einen ausreichend glatten Verlauf des Graphen zu gewährleisten. Das ist immer ein Kompromiss zwischen Berechnungszeit und Ansehnlichkeit. Testen Sie einfach ein paar verschiedene Werte, um ein Gefühl dafür zu zu bekommen. "plt.grid()" zeichnet ein Gitter in die Grafik (kann auch weggelassen werden). Die Bezeichnungen plt und np könnten auch anders gewählt werden. Es ist aber Konvention, diese so wie hier gezeigt zu wählen. <small>Mit der im obigen Bild gezeigten Menüleiste kann die dargestellte Grafik nachträglich noch geändert werden (Zoom, Pan, Achsenparameter, Kurvenparameter etc.). Natürlich kann man das alles auch direkt programmieren. Wie das funktioniert wird ansatzweise etwas später gezeigt.</small> Ein etwas komplexeres Beispiel ist Folgendes: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid() plt.show() Ausgabe: [[Datei:PythonIng_cosh4.png]] Man beachte, dass im Gegensatz zu Octave und Julia der ominöse Punkt (.) bei 2**x mit Python nicht benötigt wird. Das macht das Programmiererleben etwas einfacher. === Graphen mehrerer Funktionen und weiteres === import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x") plt.plot(x, y2, label = "sin(x) * cos(x)") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh2.png]] Um die Linienstile etwas individueller zu gestalten, ist folgender Programmcode gedacht: import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y1 = np.cosh(x) + 2**x y2 = np.sin(x) * np.cos(x) plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") plt.plot(x, y2, label = "sin(x) * cos(x)", lw=3, ls="--") plt.grid() plt.title("Funktionsgraphen") plt.xlabel("x") plt.ylabel("y") plt.legend(loc="best") plt.show() [[Datei:PythonIng_cosh3.png]] === Funktion in Parameterdarstellung === Es soll die archimedische Spirale <math>x = t \cos(t), y = t \sin(t)</math> im Intervall <math>[0, 6\pi[</math> gezeichnet werden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.show() [[Datei:PythonIng_spirale1.png]] Diese Darstellung erscheint verzerrt. Will man gleiche Achsenskalierungen, so kann man den plt.axis()-Befehl verwenden. import matplotlib.pyplot as plt import numpy as np t = np.arange(0., 6*np.pi, .1) x = t * np.cos(t) y = t * np.sin(t) plt.plot(x, y) plt.grid() plt.title("Archimedische Spirale") plt.axis("equal") plt.show() [[Datei:PythonIng_spirale2.png]] === Funktion in Polardarstellung === import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection="polar") r = np.arange(0, 1, 0.01) theta = r**3 line = ax.plot(theta, r) plt.show() [[Datei:PythonIng_polar1.png]] === Logarithmische Achsenskalierung === ==== Semilog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.semilogy() plt.show() Ausgabe: [[Datei:PythonIng_semilog1.png]] ==== LogLog ==== import matplotlib.pyplot as plt import numpy as np x = np.arange(0., 10, .1) y = 10**x plt.plot(x, y) plt.grid() plt.loglog() plt.show() [[Datei:PythonIng_loglog1.png]] === Gefüllte Fläche === import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 3, 0.1) y1 = 3*x - 1 y2 = x**2 plt.plot(x, y1, x, y2, color='black') plt.fill_between(x, y1, y2, where=y1>=y2) plt.show() [[Datei:PythonIng_gefuellt.png]] === Linien, Pfeile, Rechtecke, Kreise und Texte === import matplotlib as mpl import matplotlib.pyplot as plt fig, ax = plt.subplots() r = mpl.patches.Rectangle((0, 0), 3, 3, angle=30, fill=False) c = mpl.patches.Circle((4, 4), 2, fill=False) ax.add_patch(r) ax.add_patch(c) ax.plot([-2, 7], [-2, 0], color="black") ax.arrow(0, 7, 5, 0, length_includes_head=True, head_width=0.5, head_length=1.5, color="black") ax.set_aspect("equal") plt.axis([-3, 8, -3, 8]) plt.show() [[Datei:PythonIng_linien_pfeile_etc.png]] Text kann mit <code>ax.text(x, y, "Text")</code> hinzugefügt werden, bspw. import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") ax.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() Oder einfacher auch ohne <code>subplots</code> import matplotlib.pyplot as plt plt.text(0.1, 0.1, "Hallo") plt.text(0.5, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text1.png]] Auch Sonderzeichen (griechische Buchstaben etc.) können verwendet werden (siehe dazu auch [https://matplotlib.org/stable/users/explain/text/mathtext.html]). import matplotlib.pyplot as plt plt.text(.3, .5, r'$\Omega\ \psi\ \oint\ \nabla\ \dot a\ \frac{a}{b}\ a_b$', size="20") plt.show() [[Datei:PythonIng_text20.svg]] Jetzt wird noch gezeigt, wofür <code>subplots</code> sinnvoll eingesetzt werden können. import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(0.1, 0.1, "Hallo") ax[1].text(0.1, 0.5, "Welt", size="40", family="cursive", style="italic", rotation=30.0) plt.show() [[Datei:PythonIng_text2.png]] === Aufgaben === * Zeichnen Sie die Strophoide <math>x = \frac{a(t^2-1)}{t^2+1}, y = \frac{at(t^2-1)}{t^2+1}, a = 2, -3 \leq t \leq 3</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_strophoide.jpg]] * Zeichnen Sie die verschlungene Hypozykloide <math>x = (R-r)\cos t + c\cos\frac{R-r}{r}t, y = (R-r)\sin t - c\sin\frac{R-r}{r}t, c = 3, r = 2, R = 6, -15 \leq t \leq 15</math>. Das Ganze sollte in etwa so aussehen wie folgende Grafik: [[Datei:octave_hypozykloide.jpg]] * Testen Sie bei den obigen Übungsaufgaben verschiedene Linienstile und Farben. Farben können mit dem plt.plot()-Parameter color gewählt werden. * Testen Sie bei den obigen Übungsaufgaben verschiedene Werte für a, c, r und R. == 3D == === Räumliche Kurven === import matplotlib.pyplot as plt import numpy as np t = np.arange(0, 6*np.pi, 0.1) x = t * np.cos(t) y = t * np.sin(t) z = t fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot(x, y, z) plt.show() [[Datei:PythonIng_raumkurve1.png]] === Flächen === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z) plt.show() [[Datei:PythonIng_fläche1.png]] Das Ganze in Netzdarstellung läßt sich so programmieren: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.5) y = np.arange(0, 10, 0.5) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_wireframe(x, y, z) plt.show() [[Datei:PythonIng_fläche2.png]] Ein etwas komplexeres Beispiel: import matplotlib.pyplot as plt import numpy as np x = np.arange(0.1, 10, 0.1) y = np.arange(0.1, 10, 0.1) x, y = np.meshgrid(x, y) z1 = np.sin(x) + 3 * np.cos(y) z2 = np.sin(x) + np.log(y) z3 = x + np.cos(y) z4 = x**2 - y fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, nrows=2, ncols=2) ax[0][0].plot_surface(x, y, z1) ax[0][1].plot_surface(x, y, z2) ax[1][0].plot_surface(x, y, z3) ax[1][1].plot_surface(x, y, z4) plt.show() [[Datei:PythonIng_subplot1.png]] Man beachte, dass man die Unterbilder im Bild nach dem Ausführen des Scripts z.B. mit der mittleren Maustaste einzeln drehen, oder über die Einträge in der Menüzeile einzeln bearbeiten kann. Mit ein paar Zeilen Programmtext lässt sich also eine Menge an Funktionalität generieren. Die Farbgebung lässt sich über <code>colormaps</code> variieren. import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm) plt.show() [[Datei:PythonIng_colormap1.png]] Es gibt eine Menge an Colormaps, z.B. <code>plasma, Greys, Dark2, ocean</code>. Zwecks detaillierterer Infos siehe die matplotlib-Dokumentation. <small>Verwendet man die IDE namens IDLE, so gibt es dort auch die automatische Codevervollständigung. D.h. es werden alle Möglichkeiten (in unserem Fall nach dem Eintippen von <code>cm.</code> alle verfügbaren Colormaps) angezeigt.</small> Die "edgecolor" und Linienbreite können auch frei gewählt werden: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) ax.plot_surface(x, y, z, cmap = cm.coolwarm, edgecolor="black", linewidth=1.0) plt.show() [[Datei:PythonIng_colormap2.png]] === Höhenlinien === import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() ax.contour(x, y, z) plt.show() [[Datei:PythonIng_höhenlinien1.png|400px]] Etwas abgewandelt sieht das so aus: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contour(x, y, z) ax.clabel(hl, inline = True) plt.show() [[Datei:PythonIng_höhenlinien2.png|400px]] Und noch eine Variante (mit einem Farbbalken) sei gezeigt. import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 10, 0.1) y = np.arange(0, 10, 0.1) x, y = np.meshgrid(x, y) z = np.sin(x) + 3 * np.cos(y) fig, ax = plt.subplots() hl = ax.contourf(x, y, z) fig.colorbar(hl) plt.show() [[Datei:PythonIng_höhenlinien5.svg|400px]] === Aufgaben === * Zeichnen Sie die räumliche Kurve <math>x = 2 \cdot \cosh(t)</math>, <math>y = 5 \cdot \sin(t)</math>, <math> z = t^{2} - t</math>, <math>0 \leq t \leq 3\pi</math>. * Zeichnen Sie die Fläche <math>z = \log(x) + \cos(y)</math>. == Animationen == === Mit matplotlib === Auch mit matplotlib sind Animationen möglich. Das ist ein bisschen komplizierter und wird deshalb hier nur mit einem sehr einfachen Beispiel dargestellt (bei Interesse siehe z.B. auch das [https://matplotlib.org/stable/users/explain/animations/animations.html#animations Animations using Matplotlib-Tutorial]). import matplotlib.pyplot as plt import matplotlib.animation as ani import matplotlib import numpy as np def update(frame): line.set_xdata(x[:frame]) line.set_ydata(y[:frame]) return (line) fig, ax = plt.subplots() x = np.arange(0, 10, .1) y = np.sin(x) line, = ax.plot(x[0], y[0]) ax.set(xlim=[0, 10], ylim=[-1, 1]) a = ani.FuncAnimation(fig=fig, func=update, frames=100, interval=20) plt.show() # Speichere die Animation in einem animierten GIF (optional) a.save(filename="c:/tmp/PythonIng_anim5.gif", writer="pillow") [[Datei:PythonIng_anim5.gif]] Es wird eine Sinuskurve auf den Bildschirm gezeichnet. In der letzten Zeile wird diese Animation in ein animiertes GIF gespeichert. Das ist natürlich optional und kann auch weggelassen werden. === Mit VPython === Aber auch mit dem Modul VPython lassen sich einfache 3D-Animationen erstellen. VPython ist ein externes Modul, das vorab installiert werden muss. Unter openSUSE Tumbleweed gibt es dzt. kein entsprechendes rpm-Paket. Die übliche Methode der Installation mittels YaST oder zypper ist somit nicht möglich. Auch eine direkte Verwendung von pip führt nur zu einer Fehlermeldung (<code>error: externally-managed-environment</code>). Es empfiehlt sich dort folgende Vorgehensweise: # Erstelle zuerst eine virtuelle Umgebung, z.B.: <code>python3.11 -m venv ~/tmp/venv1</code> # Wechsle das Verzeichnis: <code>cd ~/tmp/venv1/bin</code> # Installiere das entsprechende Paket: <code>./pip install vpython</code> # Führe das entsprechende Skript aus: <code>./python ~/tmp/test1.py</code> Aktuell (März 2026) ist dieses Programmpaket lt. der [https://vpython.org/presentation2018/install.html VPython-Homepage] nur für die Python-Versionen 3.8 bis 3.12 verfügbar. Ein Beispiel zu einer einfachen Animation wird nachfolgend geliefert. from vpython import * scene.width = 1200 scene.height = 600 scene.center = vector(20,0,0) scene.background = color.white cylinder(pos=vector(0,0,0), axis=vector(20,0,0), radius=5, color=color.blue) cone(pos=vector(0,0,0), axis=vector(-10,0,0), radius=5, color=color.blue) helix(pos=vector(20,0,0), axis=vector(40,0,0), radius=2, coils=10, thickness=0.5, color=color.blue) ball = sphere(pos=vector(20,0,0), color = color.green, radius = 1) ball.p = vector(0.15, 0, 0) toc = True while True: rate(200) if(ball.pos.x <= 60 and toc == True): ball.pos += ball.p else: toc = False ball.pos -= ball.p if(ball.pos.x <= 20 and toc == False): toc = True [[Datei:PythonIng_vpython_anim.JPG]] Idealerweise öffnet sich beim Ausführen des Scripts ein Browserfenster. Darin wird die programmierte Animation gezeigt (siehe auch den obigen Screenshot). Eine Größenänderung können Sie mit der mittleren Maustaste initiieren. Die Szenerie drehen können Sie mit der rechten Maustaste. === Mit VTK === Komplexer, aber auch mächtiger als VPython ist die Verwendung von VTK ('''V'''isualization '''T'''ool'''k'''it). Genauer gesagt des Python-Wrappers von VTK. Dieses externe Python-Modul muss vorab installiert werden (z.B. mittels YaST, pip oder in eine virtuelle Umgebung). VTK ist eine Softwarebibliothek zur 3D-Visualisierung und wurde ursprünglich in C++ geschrieben. Verbreitet eingesetzt wird diese Bibliothek in der Wissenschaft und Forschung, z.B. * in der medizinischen Bildgebung * für Strömungssimulationen * für Klimadaten VTK funktioniert nach dem {{W|Grafikpipeline|Pipeline-Prinzip}}: Source (Quellen) -> Filter -> Mapper (Senken) -> Actor/Renderer Daten fließen von den Quellen zu den Senken. Als einfaches Beispiel wird die Darstellung eines Zylinders gezeigt, der mit den Maustasten gedreht oder in der Größe geändert werden kann: import vtk # Zylinder erzeugen cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) # Geometrie in darstellbare Daten umwandeln mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) # Objekt in der Szene actor = vtk.vtkActor() actor.SetMapper(mapper) # Szene verwalten renderer = vtk.vtkRenderer() renderer.AddActor(actor) # Render-Fenster render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) # Maus/Tastatur-Steuerung interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Starten render_window.Render() interactor.Start() Ausgabe: [[Datei:PythonIng_VTK_1.png]] Gleiches Beispiel wie oben, aber mit einer Animationssequenz: import vtk import time cyl = vtk.vtkCylinderSource() cyl.SetRadius(5.0) cyl.SetHeight(20.0) cyl.SetResolution(40) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(cyl.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) renderer = vtk.vtkRenderer() renderer.AddActor(actor) render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) render_window.Render() time.sleep(0.01) Das Grafikfenster schließt sich nach Ablauf der Schleife. Das Fenster bleibt geöffnet, wenn Sie am Programmende folgenden Befehl hinschreiben interactor.Start() Um den animierten Zylinder grün einzufärben, müssen Sie Folgendes im obigen Programm ergänzen (Farbnamen): colors = vtk.vtkNamedColors() actor.GetProperty().SetColor(colors.GetColor3d("Green")) Als Namen können Sie u.a. die CSS3 Web-Farben verwenden (siehe z.B. [https://wiki.selfhtml.org/wiki/Farbe/Farbangaben] und {{W|Webfarbe#CSS_3}}). Alternativ funktioniert auch das ({{W|RGB-Farbraum|RGB}}): actor.GetProperty().SetColor(0.0, 0.6, 0.0) Wie der Zylinder mit einer Textur versehen wird, zeigt folgendes Programm: import vtk import time cylinder = vtk.vtkCylinderSource() cylinder.SetResolution(30) cylinder.SetHeight(3.0) cylinder.SetRadius(1.0) cylinder.CappingOn() texture_coords = vtk.vtkTextureMapToCylinder() texture_coords.SetInputConnection(cylinder.GetOutputPort()) texture_coords.PreventSeamOn() reader = vtk.vtkJPEGReader() reader.SetFileName("PythonIng_textur.jpg") texture = vtk.vtkTexture() texture.SetInputConnection(reader.GetOutputPort()) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(texture_coords.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.SetTexture(texture) renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(renderWindow) renderer.AddActor(actor) for i in range(360): actor.RotateZ(1) actor.RotateY(.5) renderWindow.Render() time.sleep(0.01) interactor.Start() <gallery> PythonIng_textur.jpg | Textur-Datei PythonIng_VTK_2.png | Ausgabe (Screenshot) </gallery> Nun aber genug von VTK und der Erstellung von Grafiken, weiter geht es mit mathematischeren Themen. = Vektoren und Matrizen = == Zahlenfolgen == Für das Erstellen von Zahlenfolgen bieten sich die Funktionen <code>arange</code> und <code>linspace</code> aus dem <code>numpy</code>-Modul an. from numpy import * start = 0 stop = 10 step = 2 num = 10 r = arange(start, stop, step) # step ... Schrittweite l = linspace(start, stop, num) # num ... Anzahl der Werte print("r = ", r) print("l = ", l) Ausgabe: r = [0 2 4 6 8] l = [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556 6.66666667 7.77777778 8.88888889 10. ] Bei <code>arange</code> ist der <code>stop</code>-Wert nicht im Ergebnis enthalten, bei <code>linspace</code> aber sehr wohl. == Vektoren == Vektoren sollten jedem aus der Linearen Algebra bekannt sein. === Arrays === In Python mit NumPy kann man Vektoren durch die Funktion array erzeugen. import numpy as np l1 = (-5, 3, 2) l2 = (1, 1, 4) a1 = np.array(l1) a2 = np.array(l2) a3 = a1 + a2 a4 = 2 * a2 print(a1) print(a2) print(a3) print(a3[2]) print(a4) Ausgabe: [-5 3 2] [1 1 4] [-4 4 6] 6 [2 2 8] === Zeilen- und Spaltenvektoren === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) print(z) print(s) Ausgabe: [ [-5 3 2] ] [[1] [1] [4]] === Skalarprodukt === import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) skalarprodukt = np.dot(a1, a2) print(skalarprodukt) Ausgabe: 6 === Vektorprodukt === <math>a\ast b=\left(\begin{array}{c} a_{1}\\ a_{2}\\ a_{3} \end{array}\right)\ast\left(\begin{array}{c} b_{1}\\ b_{2}\\ b_{3} \end{array}\right)=\left(\begin{array}{c} a_{2}b_{3}-a_{3}b_{2}\\ a_{3}b_{1}-a_{1}b_{3}\\ a_{1}b_{2}-a_{2}b_{1} \end{array}\right) </math> Python-Code: import numpy as np a1 = np.array((-5, 3, 2)) a2 = np.array((1, 1, 4)) vektorprodukt = np.cross(a1, a2) print(vektorprodukt) Ausgabe: [10 22 -8] === Transponierter Vektor === import numpy as np # Zeilenvektor z = np.array([ [-5, 3, 2] ]) # Spaltenvektor s = np.array([[1], [1], [4]]) # transponierter Vektor z_tp = np.transpose(z) # transponierter Vektor s_tp = np.transpose(s) print(z_tp) print(s_tp) Ausgabe: [[-5] [ 3] [ 2]] [ [1 1 4] ] === Vektorfelder visualisieren === import matplotlib.pyplot as plt import numpy as np # Daten generieren x = np.arange(0, 10, 1) y = np.arange(0, 10, 1) X, Y = np.meshgrid(x, y) U = X * Y V = Y + X # Plotten fig, ax = plt.subplots() ax.quiver(X, Y, U, V, angles='xy') plt.show() Ausgabe: [[Datei:PythonIng_quiver1.png]] == Matrizen== import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) print(m1) Ausgabe: [[1 2 3] [4 5 6]] === Zugriff auf Matrizenelemente === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) # Element aus Zeile 2 und Spalte 3 (Achtung! Index startet bei Null) print(m1[1,2]) Ausgabe: 6 === Addition und Subtraktion von Matrizen === import numpy as np m1 = np.matrix([[1, 2, 3], [4, 5, 6]]) m2 = np.matrix([[0, 0, 2], [1, 3, 2]]) print(m1 + m2) print(m1 - m2) Ausgabe: [[1 2 5] [5 8 8]] [[1 2 1] [3 2 4]] === Transponierte Matrix === import numpy as np m = np.matrix([[1, 2, 3], [4, 5, 6]]) mt = np.transpose(m) print(m) print(mt) Ausgabe: [[1 2 3] [4 5 6]] [[1 4] [2 5] [3 6]] === Rang einer Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) rg = np.linalg.matrix_rank(m) print(rg) Ausgabe: 2 === Inverse Matrix === import numpy as np m = np.matrix([[1, 3], [0, -5]]) mi = np.linalg.inv(m) print(mi) Ausgabe: [[ 1. 0.6] [-0. -0.2]] === Multiplikation von Matrizen (falksches Schema) === import numpy as np m1 = np.matrix([[1, 3, 4], [0, -5, 1]]) m2 = np.matrix([[1, 2], [2, 3], [0, 2]]) print(m1 @ m2) Ausgabe: [[ 7 19] [-10 -13]] === Eigenwerte und Eigenvektoren === import numpy as np m = np.matrix([[5, 8], [1, 3]]) D,V = np.linalg.eig(m) # Eigenwerte print(D) # Eigenvektoren print(V) Ausgabe: [7. 1.] [[ 0.9701425 -0.89442719] [ 0.24253563 0.4472136 ]] === Teilmatrizen === import numpy as np m = np.matrix([[1, 3, 4], [0, -5, 1]]) print("m = ", m) # Erste Zeile extrahieren m1 = m[0,:] print("m1 = ", m1) # Das Element aus der 1. Zeile und der 2. Spalte extrahieren m2 = m[0,1] print("m2 = ", m2) # Zweite Spalte extrahieren m3 = m[:, 1] print("m3 = ", m3) Ausgabe: m = [[ 1 3 4] [ 0 -5 1]] m1 = [ [1 3 4] ] m2 = 3 m3 = [[ 3] [-5]] === Spezielle Matrizen === ==== Nullmatrix ==== import numpy as np z = np.zeros((3, 2)) print(z) Ausgabe: [[0. 0.] [0. 0.] [0. 0.]] ==== Einheitsmatrix ==== import numpy as np z = np.eye(3) print(z) Ausgabe: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] ==== Matrix mit lauter Einsen ==== import numpy as np z = np.ones((3, 2)) print(z) Ausgabe: [[1. 1.] [1. 1.] [1. 1.]] === Spärlich besetzte Matrizen === Das Thema spärlich besetzter Matrizen wird hier nur kurz angerissen. Nähere Details siehe unter dem Weblink [https://docs.scipy.org/doc/scipy/reference/sparse.html#module-scipy.sparse]. import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 = Lineare Gleichungssysteme = Sei <math>Ax = b</math> ein lineares Gleichungssystem. <math>A</math> sei die Koeffizientenmatrix, <math>x</math> der Lösungsvektor und <math>b</math> ein bekannter Vektor. Beispiel: import numpy as np A = np.array([[5, 1], [0, 2]]) b = np.array([1, 2]) x = np.linalg.solve(A, b) print(x) Ausgabe: [0. 1.] == Aufgabe == * Lösen Sie folgendes Gleichungssystem mittels Python (und zur Kontrolle auch händisch): 5x + 6y - 2z = 12 3x - y - 3z = 6 2x + 2y + 4z = 5 = Polynome = == Ein erstes einfaches Beispiel == Gegeben sei das Polynom <math>7x^3+5x^2+1</math>. In Python: import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p) Ausgabe: 3 2 7 x + 5 x + 1 == Einzelne Polynomwerte berechnen == import numpy as np p = np.poly1d([7, 5, 0, 1]) print(p(1.5)) Ausgabe: 35.875 == Polynome integrieren und differenzieren == import numpy as np p = np.poly1d([7, 5, 0, 1]) # 1. Ableitung p1 = p.deriv() p2 = p.deriv(1) # 2. Ableitung p3 = p.deriv(2) # Integral p4 = p.integ() print(p1) print(p2) print(p3) print(p4) Ausgabe: 2 21 x + 10 x 2 21 x + 10 x 42 x + 10 4 3 1.75 x + 1.667 x + 1 x == Nullstellen bestimmen == import numpy as np p = np.poly1d([2, 5, 0, 4]) r = np.roots(p) print(r) Ausgabe: [-2.7621427 +0.j 0.13107135+0.84077099j 0.13107135-0.84077099j] == Aufgaben == * Berechnen Sie den Wert für x = 3 des Polynoms <math>y = 2x^4 - 3x^3 - x + 7</math>. * Differenzieren und integrieren Sie das Polynom <math>y = 2x^4 - 3x^3 - x + 7</math>. * Berechnen Sie die Nullstellen von <math>y = 7x^5 - 3x^2 + 12</math>. = Nichtlineare Gleichungen und Gleichungssysteme = == Nullstellenbestimmung == Löse eine beliebige Gleichung f(x) = 0, z.B. <math> f(x) = x^2 - 5\cos(x) - 10 = 0 </math>: import scipy import numpy as np def f(x): return x**2 - 5*np.cos(x) - 10 xstart = [-1, 1] # Startwerte xn = scipy.optimize.root(f, xstart) print(xn.x) Ausgabe: [-2.46813009 2.46813009] Funktionsgraph: [[Datei:octave_nichtlin2.jpg]] == Gleichungssysteme == SymPy ist ein externes Modul, das symbolisches Rechnen ('''Sym'''bolic '''Py'''thon) ermöglicht. Folgende Aufgabe ist dem Buch "Knorrenschild: Numerische Mathematik, Hanser, 2017, Seite 72" entnommen. Zu lösen ist das nichtlineare Gleichungssystem <math>f_1 = 2x_1 + 4x_2 = 0 </math> <math>f_2 = 4x_1 + 8x_2^3 = 0</math> Mit Python ist das so möglich: import sympy x1, x2 = sympy.symbols("x1 x2") f1 = 2*x1 + 4*x2 f2 = 4*x1 + 8*x2**3 s = sympy.solve((f1, f2), x1, x2) print(s) Ausgabe: [(-2, 1), (0, 0), (2, -1)] Plot: [[Datei:IngPython_nl_gleichung1.svg|500px]] = Komplexe Zahlen = Die imaginäre Einheit wird in Python durch den Buchstaben <code>j</code> symbolisiert. Darstellen kann man eine komplexe Zahl bekannterweise in mehreren Formen: * Kartesische Darstellung <math>z = \Re(z) + j \cdot \Im(z)</math> * Polardarstellungen <math>z = r \cdot (\cos(\phi) + j \cdot \sin(\phi)) = r \cdot e^{j\cdot \phi}</math> Die konjugiert komplexe Zahl ist <math>z^* = \Re(z) - j \cdot \Im(z)</math> Nachfolgend einige mathematische Operationen mit Python und NumPy. import numpy as np z1 = 2 + 5j # kartesische Darstellung z2 = 3 * np.exp(3j) # Polardarstellung # Addition res = z1 + z2 print("z1 + z2 = ", res) # Multiplikation res = z1 * z2 print("z1 * z2 = ", res) # Realteil res = np.real(z2) print("Realteil von z2 = ", res) # Imaginärteil res = np.imag(z2) print("Imaginaerteil von z2 = ", res) # Betrag res = np.abs(z1) print("Betrag von z1 = ", res) # Argument res = np.angle(z1) print("Argument von z1 = ", res) # Konjugiert komplexe Zahl res = np.conj(z1) print("Konjugiert komplexe Zahl von z1 = ", res) Ausgabe: z1 + z2 = (-0.9699774898013365+5.423360024179601j) z1 * z2 = (-8.05675510050068-14.003167400647481j) Realteil von z2 = -2.9699774898013365 Imaginaerteil von z2 = 0.4233600241796016 Betrag von z1 = 5.385164807134504 Argument von z1 = 1.1902899496825317 Konjugiert komplexe Zahl von z1 = (2-5j) = Interpolation = import numpy as np import scipy import matplotlib.pyplot as plt # Stützpunkte xp = np.arange(1, 6) yp = (0, -5, 2, 7, 6) ti = np.arange(1, 5, 0.01) i1 = scipy.interpolate.interp1d(xp, yp, kind = "linear") i2 = scipy.interpolate.interp1d(xp, yp, kind = "cubic") plt.plot(xp, yp, "rx") plt.plot(xp, i1(xp)) plt.plot(ti, i2(ti)) plt.show() Ausgabe: [[Datei:PythonIng_interpol1.png]] = Differenzialrechnung = == Numerisches Differenzieren == Als Beispiel differenzieren wir <math>y = 5x\sin{x}</math> und stellen das Ganze grafisch dar. from findiff import Diff import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10, 1000) f = 5 * x * np.sin(x) dx = x[1] - x[0] # Ableitung d_dx = Diff(0, dx) df_dx = d_dx(f) # Grafik plt.plot(x, f, label = "y") plt.plot(x, df_dx, label = "y'") plt.grid() plt.legend(loc="best") plt.show() Ausgabe: [[Datei:octave_diff1.jpg]] <small>findiff ist ein externes Modul. Dieses muss installiert werden (z.B. so: ...\Python\Scripts\pip.exe install --upgrade findiff). Für die Vorgehensweise unter openSUSE Tumbleweed siehe das Kapitel [[Ing_Mathematik:_Python#Mit_VPython | VPython]], nur dass das Ganze mit einer aktuelleren Python-Version exekutiert wird, z.B. mit Python 3.13. Das im Buch "Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler, Rheinwerk" verwendete Modul "scipy.misc" ist veraltet (deprecated ... missbilligt). Lt. [https://docs.scipy.org/doc/scipy-1.17.0/dev/roadmap-detailed.html#misc SciPy-Dokumentation für die Version 1.17.0] wurden alle entsprechenden Features schon entfernt.</small> == Symbolisches Differenzieren == Differenzieren Sie die Funktionen <math>f_1(x) = x^2</math> und <math>f_2(x) = \sin(x)\cos\left(\frac{x}{2}\right)</math>. import sympy x = sympy.symbols("x") f1 = x**2; f2 = sympy.sin(x) * sympy.cos(x/2.) d1 = sympy.diff(f1, x) d2 = sympy.diff(f2, x) print(d1) print(d2) Ausgabe: 2*x -0.5*sin(0.5*x)*sin(x) + cos(0.5*x)*cos(x) == Aufgaben == * Differenzieren Sie die Funktion <math>y = \log(x) + 10x</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. * Differenzieren Sie die Funktion <math>y = \frac{\sinh(x)}{(1+x)}</math> und stellen Sie y, sowie y' grafisch am Bildschirm dar. = Integralrechnung = == Numerisches Integrieren == Berechnen Sie das Integral <math>\int_{0}^{3}x^2 dx</math>. import scipy def f(x): return x**2 i = scipy.integrate.quad(f, 0, 3) print(i) Ausgabe: (9.000000000000002, 9.992007221626411e-14) Das trifft den exakten Wert 9.0 ziemlich genau. Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} dx</math>. import scipy import numpy as np def f(x): return 2**(-x) i = scipy.integrate.quad(f, 0, np.inf) print(i) Ausgabe: (1.4426950408889556, 4.486558477977586e-09) == Symbolisches Integrieren == Berechnen Sie <math>\int x^2 \text{d}x</math> und <math>\int \sin{x}\cos{\frac{x}{2}} \text{d}x</math>. import sympy x = sympy.symbols("x") f1 = x**2 f2 = sympy.sin(x) * sympy.cos(x/2.) i1 = sympy.integrate(f1, x) i2 = sympy.integrate(f2, x) print(i1) print(i2) Ausgabe: x**3/3 -0.666666666666667*sin(0.5*x)*sin(x) - 1.33333333333333*cos(0.5*x)*cos(x) Berechnen Sie das Integral <math>\int_{0}^{\infty} 2^{-x} \text{d}x</math>. import sympy x = sympy.symbols("x") f = 2**(-x) i = sympy.integrate(f, (x, 0, sympy.oo)) print(i) Ausgabe: 1/log(2) <code>sympy.oo</code> steht für das {{W|Unendlichzeichen}} <math>\infty</math> (die liegende Acht oder das Möbiusband). Mit <code>sympy.pprint(i)</code> ließe sich letzere Ausgabe etwas schöner schreiben: 1 ────── log(2) Man beachtete, <code>log</code> steht hier für den natürlichen Logarithmus <code>ln</code>. == Aufgaben == * Integrieren Sie die Funktion <math>y = \log(x) + 10x</math> von 1 bis 5. * Integrieren Sie die Funktion <math>y = x^3</math> von 0 bis 4. * Integrieren Sie <math>\int x^x(\log (x) + 1)\mathrm dx</math> symbolisch. = Gewöhnliche Differenzialgleichungen = == DGL numerisch lösen == Für die Lösung von Differenzialgleichungen steht u.a. die Funktion scipy.integrate.solve_ivp() zur Verfügung. Diese Funktion implementiert auch das Runge-Kutta-Verfahren (RK45). {{Wikipedia | Runge-Kutta-Verfahren}} Beispiel <math>y' = x^2 + y^3</math>: import scipy import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x**2 + y**3 y0 = [1] xi = [0, 1] x = np.arange(0, 1, 0.01) z = scipy.integrate.solve_ivp(dy_dx, xi, y0, method="RK45", dense_output=True) y = z.sol(x) plt.plot(x, y.T) plt.grid() plt.show() [[Datei:PythonIng_dgl1.png]] == DGL symbolisch lösen == Beispiel <math>y' = x^2 + y^3</math>: import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) print(lsg) Ausgabe: [Eq(f(x), (-x**2)**(1/3)), Eq(f(x), (-x**2)**(1/3)*(-1 - sqrt(3)*I)/2), Eq(f(x), (-x**2)**(1/3)*(-1 + sqrt(3)*I)/2)] Mit <code>sympy.pprint</code> (pretty print) lässt sich die Ausgabe etwas übersichtlicher darstellen. import sympy x = sympy.symbols("x") y = sympy.Function("f")(x) dgl = x**2 + y**3 lsg = sympy.dsolve(dgl, y) sympy.pprint(lsg) Ausgabe: ⎡ _____ _____ ⎤ ⎢ _____ 3 ╱ 2 3 ╱ 2 ⎥ ⎢ 3 ╱ 2 ╲╱ -x ⋅(-1 - √3⋅ⅈ) ╲╱ -x ⋅(-1 + √3⋅ⅈ)⎥ ⎢f(x) = ╲╱ -x , f(x) = ────────────────────, f(x) = ────────────────────⎥ ⎣ 2 2 ⎦ == Aufgaben == * Lösen Sie die Differenzialgleichung <math>y' = \frac{1}{x\cdot y}</math> mit Python. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>m' = -k\cdot m</math>. Kontrollieren Sie das Ergebnis, indem Sie die DGl händisch lösen. * Lösen Sie die Differenzialgleichung <math>y' = \sqrt{|y|}</math>. =Laplace-Transformation= Laplace-Transformation: <math>F(s) =\mathcal{L} \left\{f\right\}(s) = \int_{0}^{\infty} f(t) \mathrm e^{-st} \,\mathrm{d}t, \qquad s\in\mathbb{C} </math> Inverse Laplace-Transformation: <math>\mathcal{L}^{-1} \left\{F\right\}(t) = \frac{1}{2 \pi \mathrm j} \int_{ \gamma - \mathrm j \infty}^{ \gamma + \mathrm j \infty} \mathrm e^{st} F(s)\,\mathrm ds = \begin{cases} f(t) & \text{für } t \geq 0 \\ 0 & \text{für } t < 0 \end{cases} </math> Siehe auch [[Ing_Mathematik:_Laplace-Transformation]] Code: import sympy from sympy.abc import t, s # Laplace-Transformation der Funktion f(t) = 1 (Heaviside-Fkt.) f = 1 # alternativ: f = sympy.Heaviside(t) F = sympy.laplace_transform(f, t, s, noconds=True) print("Laplace-Transformierte F(s):", F) # Inverse Laplace-Transformation zurück in den Zeitbereich f_inv = sympy.inverse_laplace_transform(F, s, t) print("Inverse Transformation f(t):", f_inv) Ausgabe: Laplace-Transformierte F(s): 1/s Inverse Transformation f(t): Heaviside(t) Die Zeile from sympy.abc import t, s steht alternativ für t = sympy.symbols("t") s = sympy.symbols("s") =Fourier-Reihen= <math> f(x)\approx \frac{a_{0}}{2}+\sum_{k=1}^{\infty}\left(a_{k}\cos\left(kx\right)+b_{k}\sin\left(kx\right)\right) </math> <math> a_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\cos\left(kx\right)\mathrm dx\quad\text{für }k\geq0 </math> <math> b_{k} = \frac{1}{\pi}\int_{-\pi}^{\pi}f(x)\cdot\sin\left(kx\right)\mathrm dx\quad\text{für }k\geq1 </math> Für die Sägezahnfunktion <math>y=x;\, 0 < x < 2\pi</math> sei die Fourierreihe mit einem Python-Programm (unter Mithilfe von sympy) hergeleitet. Code: from sympy import fourier_series, pi, symbols, pprint x = symbols('x') f = x s = fourier_series(f, (x, 0, 2*pi)) pprint(s.truncate(n=4)) Ausgabe: 2⋅sin(3⋅x) -2⋅sin(x) - sin(2⋅x) - ────────── + π 3 Siehe auch [[Ing Mathematik: Fourierreihen]]. Ein komplizierteres Beispiel: [[Datei:IngMath fourier bsp13.svg | 300px]] <math>0\le t < T/2\text{:}\quad f(t) = H</math> <math>T/2 \le t \le T\text{:}\quad f(t) = \frac{2H}{T}\left( t-\frac{T}{2}\right)</math> Code: import sympy as sp H = sp.Symbol('H', positive=True) T = sp.Symbol('T', positive=True) t = sp.Symbol('t') f = sp.Piecewise( (H, (t > 0) & (t < T/2)), (2*H/T*(t-T/2), (t > T/2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Ausgabe: ⎛2⋅π⋅t⎞ ⎛4⋅π⋅t⎞ ⎛6⋅π⋅t⎞ ⎛2⋅π⋅t⎞ ⎛6⋅π⋅t⎞ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ H⋅sin⎜─────⎟ 2⋅H⋅cos⎜─────⎟ 2⋅H⋅cos⎜─────⎟ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ ⎝ T ⎠ 3⋅H ──────────── - ──────────── + ──────────── + ────────────── + ────────────── + ─── π 2⋅π 3⋅π 2 2 4 π 9⋅π =Rechnen mit wirklich großen Zahlen= Bekannt ist, dass Python kaum Einschränkungen beim Wertebereich von Ganzzahlen hat, z.B. print(10**300) Ausgabe (gekürzt): 100000000000000000000...00000000000000000000000000000000000000000000000000000000000000000000000 Ähnliches geht auch mit Gleitpunktzahlen, z.B. durch die Verwendung des Moduls mpmath: import mpmath print(mpmath.mpf(1500.4)**mpmath.mpf(300)) Ausgabe: 7.27975299218612e+952 Anderes Beispiel: from mpmath import mp, pi mp.dps = 100 print(pi) Ausgabe: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 mpmath kann noch einiges mehr, dazu sei aber auf die entsprechende Dokumentation auf der mpmath-Homepage verwiesen. mpmath ist Bestandteil von SymPy, kann aber auch separat installiert werden. Aber auch Python selbst besitzt eine Möglichkeit, um mit großen bzw. exakten Gleitpunktzahlen zu rechnen, nämlich das interne Modul decimal. Dieses hat einige Vorteile gegenüber mpmath, aber auch gravierende Nachteile. Diese seien hier nicht detailliert aufgezählt. Grob gesagt hat decimal im Finanzwesen seine Berechtigung. Für wissenschaftliche Anwendungen wird aber mpmath vorzuziehen sein, da es u.a. vielfältige mathematische Funktionen bereit stellt. Nachfolgend ein einfaches Beispiel mit decimal: import decimal print("Potenzierung:", decimal.Decimal(1500.4) ** decimal.Decimal(300.0)) print("Einfache Addition:", 0.1 + 0.2) decimal.getcontext().prec = 50 print("Addition mit decimal:", decimal.Decimal("0.1") + decimal.Decimal("0.2")) Ausgabe: Potenzierung: 7.279752992186121551039839134E+952 Einfache Addition: 0.30000000000000004 Addition mit decimal: 0.3 <u>Aufgabe:</u> Recherchieren Sie im Internet die genauen Vor- und Nachteile von decimal und mpmath. Verwenden Sie dazu auch KI (z.B. von Google, chatgpt). =Regelungstechnische Aufgabenstellungen= Für regelungstechnische Aufgaben gibt es u.a. das externe Paket <code>control</code>. Hier soll nicht detailliert darauf eingegangen werden. Anhand eines Beispiels soll anschließend nur die Visualisierung in Form eines Bode-Diagramms und der Sprungantwort gezeigt werden. Gegeben sei ein P-Regler mit <math>R = \frac{5}{2}</math> und eine Strecke <math>S= \frac{1}{30s^3+20s^2+10s+1,5}</math>. Gesucht sei vorerst ein Bode-Diagramm für den offenen Regelkreis und das Führungsverhalten. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke # oder: G0 = ct.series(regler, strecke) Gw = ct.feedback(G0) ct.bode_plot(G0, label='G0') ct.bode_plot(Gw, label='Gw') plt.show() [[Datei:PythonIng_bode1.svg]] Nun noch für obiges Beispiel die Sprungantwort. Diese zeigt einige große Überschwinger, d.h. der Regler kann sicher noch optimiert werden. import numpy as np import control as ct import matplotlib.pyplot as plt zaehler1 = np.array([1.]) nenner1 = np.array([30., 20., 10., 1.5]) strecke = ct.tf(zaehler1, nenner1) zaehler2 = np.array([5.]) nenner2 = np.array([2.]) regler = ct.tf(zaehler2, nenner2) G0 = regler*strecke Gw = ct.feedback(G0) t, y = ct.step_response(Gw) plt.plot(t,y) plt.title('Sprungantwort') plt.xlabel('t') plt.ylabel('h(t)') plt.grid() plt.show() [[Datei:PythonIng_bode3.svg]] Einige weitere wichtige Daten (Phasenreserve, Amplitudenreserve, Durchtrittsfrequenz) lassen sich mittels der <code>control</code>-Funktion <code>margin()</code> ermitteln. Die Ortskurve lässt sich mit der Funktion <code>nyquist_plot()</code> zeichnen. Dies sei hier aber nicht weiter ausgeführt. ==Aufgaben== * Zeichen Sie mit Python die Ortskurve für obiges Beispiel. * Was passiert, wenn man die Reglerverstärkung weiter aufdreht (z.B. auf <math>R = \frac{25}{2}</math>)? * Wie sehen das Bode-Diagramm und die Sprungantwort aus, wenn ein PI-Regler verwendet wird? = Stereostatik etc. = Das Modul SymPy bietet einige Möglichkeiten einfache Bauwerke zu berechnen, z.B. Balken oder Fachwerke. Nachfolgend wird ein einfaches Fachwerk berechnet und gezeichnet. Python-Code: from sympy.physics.continuum_mechanics.truss import Truss t = Truss() # Knoten t.add_node(("A", -3, 0), ("B", 0, 0), ("C", 4, 0), ("D", 7, 0), ("E", 6, 1.5), ("F", 2, 3), ("G", -2, 1.5)) # Stäbe t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) t.add_member(("FE","F","E"), ("DE","D","E")) # Auflager; roller ... Loslager, pinned ... Festlager t.apply_support(("A","roller"), ("D","pinned")) # Einwirkende Kräfte t.apply_load(("G", 5, 270), ("E", 3, 90)) # Berechnung t.solve() print("Reaction Forces: ", t.reaction_loads) print("Internal Forces: ", t.internal_forces) # Fachwerk zeichnen p = t.draw() p.show() Ausgabe auf der Konsole: Reaction Forces: {'R_A_y': 4.20000000000000, 'R_D_x': 0, 'R_D_y': -2.20000000000000} Internal Forces: {'AB': 2.80000000000000, 'BC': 0.333333333333333, 'CD': -1.46666666666667, 'AG': -5.04777178564958, 'GB': -2.05555555555556, 'GF': -1.23413387432364, 'BF': 0.411111111111111*sqrt(13), 'FC': -0.3*sqrt(13), 'CE': 1.50000000000000, 'FE': 0.284800124843917, 'DE': 2.64407093534026} Zeichnung: [[File:PythonIng_fachwerk1.svg|300px]] Details zu diesem Thema siehe z.B. [https://docs.sympy.org/latest/modules/physics/continuum_mechanics/index.html Continuum Mechanics] oder [https://docs.sympy.org/latest/tutorials/physics/continuum_mechanics/index.html Continuum Mechanics Tutorials]. Auch andere mechanische Probleme werden von SymPy abgehandelt ([https://docs.sympy.org/latest/tutorials/physics/index.html Physics Tutorials]). == Aufgabe == Gegeben sei ein einseitig eingespannter Kragträger. Belastet wird er durch eine Einzellast am Trägerende. Für die Daten siehe folgende ASCII-Skizze: | 20 kN //|---> x | //| V //|---------------------- //| 10 m | Elastizitätsmodul E = 2,1*10⁵ N/mm² Flächenträgheitsmoment I = 0.001 m⁴ Berechnen Sie die Auflagerreaktionen, den Querkraft- und Biegemomentenverlauf, sowie die Verformungen. Stellen Sie dies mit Hilfe von SymPy graphisch und auch mittels Formeln dar. Verwenden Sie dazu auch pprint (pretty print) aus dem SymPy-Modul. Zwecks Lösungsansatz siehe die oben aufgeführte Seite "Continuum Mechanics Tutorials". Achten Sie auch auf die Einheiten! Kontrollieren Sie das Ganze mittels händischer Rechnung. In dem genannten Tutorial ist von "Singularity Functions" die Rede. Gemeint ist damit in diesem Kontext die {{W|Föppl-Klammer}}. Einige Python-Programme, vorrangig zu Maschinenelementen, finden sich auf [https://baymp.de/download_python.html BayMP für Python] (Balken, Zahnräder, Stabknickung usw.). =Thermodynamik= == PYroMat == Für thermodynamische Aufgabenstellungen gibt es verschiedene externe Module. Eines davon ist PYroMat (siehe auch [http://pyromat.org]). Damit lassen sich thermodynamische Stoffdaten für viele Substanzen berechnen. Beispiel (einige Stoffdaten für Wasser bei 400°C und 20 bar berechnen): import pyromat as pm # Wasserdaten laden: H2O = pm.get('mp.H2O') # Stoffdaten berechnen: T = 673.15 # Temperatur in Kelvin p = 20 # Druck in bar v = H2O.v(T, p) h = H2O.h(T, p) s = H2O.s(T, p) print(f"Spezifisches Volumen: {v} m³/kg") print(f"Spezifische Enthalpie: {h} kJ/kg") print(f"Spezifische Entropie: {s} kJ/(kg K)") Ausgabe: Spezifisches Volumen: [0.1512163] m³/kg Spezifische Enthalpie: [3248.3789473] kJ/kg Spezifische Entropie: [7.12924142] kJ/(kg K) <small> PYroMat muss vorab installiert werden (z.B. mittels pip, in eine virtuelle Umgebung) </small> <code>mp</code> steht für "multi phase". Für ein ideales Gas wäre <code>ig</code> zuständig, z.B. <code>'ig.O2'</code>. Beispiel (T-s-Diagramm für Wasser zeichnen): import numpy as np import matplotlib.pyplot as plt import pyromat as pm # Konfigurieren pm.config["unit_pressure"] = "bar" pm.config["unit_temperature"] = "K" fluid = pm.get("mp.H2O") # Temperaturbereich für das Nassdampfgebiet T_tripel = 273.16 T_crit = 647.096 T = np.linspace(T_tripel, T_crit - 0.1, 200) # Sättigungslinien berechnen und zeichnen for x in np.linspace(0.0, 1.0, 5): s = fluid.s(T=T, x=x) if(x<=0.0): plt.plot(s, T, label="Siedelinie x=%3.1f" % x, linewidth=2.0) elif(x>=1.0): plt.plot(s, T, label="Taulinie x=%3.1f" % x, linewidth=2.0) else: plt.plot(s, T, label="x=%3.1f" % x, linewidth=1.0) # Isobaren zeichnen p_values = [0.1, 1, 10, 50, 100] T_isobar = np.linspace(T_tripel, 1000, 200) t = 0.7 for p in p_values: s_iso = fluid.s(T=T_isobar, p=p) plt.plot(s_iso, T_isobar, 'k-', alpha=0.8, linewidth=0.8) t += .05 idx = int(len(s_iso) * t) plt.text(s_iso[idx], T_isobar[idx], f"{p} bar", fontsize=9, alpha=0.8) # Diagramm zeichnen plt.title("T-s-Diagramm für Wasser") plt.xlabel("Spezifische Entropie s in kJ/kg K", fontsize=10) plt.ylabel("Temperatur T in K", fontsize=10) plt.legend(loc="best") plt.grid(True) plt.show() Ausgabe (in etwa so): [[Datei:T-s-Diagramm fuer Wasser.svg|400px]] == CoolProp == Auch mit CoolProp können Stoffdaten berechnet werden. Siehe auch [https://coolprop.org/coolprop/wrappers/Python/index.html] Beispiel (Wasser bei 20bar und 400°C): import CoolProp.CoolProp as CP fluid = 'Water' T = 673.15 # Temperatur in Kelvin P = 20e5 # Druck in Pascal dichte = CP.PropsSI('D', 'T', T, 'P', P, fluid) enthalpie = CP.PropsSI('H', 'T', T, 'P', P, fluid) entropie = CP.PropsSI('S', 'T', T, 'P', P, fluid) print(f"Spez. Volumen: {1/dichte:.6f} m³/kg") print(f"Spez. Enthalpie: {enthalpie:.2f} J/kg") print(f"Spez. Entropie: {entropie:.2f} J/kgK") Ausgabe: Spez. Volumen: 0.151215 m³/kg Spez. Enthalpie: 3248344.02 J/kg Spez. Entropie: 7129.16 J/kgK == iapws == Um Werte für Wasser(dampf) zu erhalten (IAPWS; '''I'''nternational '''A'''ssociation for the '''P'''roperties of '''W'''ater and '''S'''team) gibt es die Bibliothek iapws. Siehe auch [https://iapws.org/] und [https://pypi.org/project/iapws/] Beispiel (Wasser für 20bar und 400°C): from iapws import IAPWS97 dampf = IAPWS97(P=2.0, T=673.15) print(f"Spezifisches Volumen: {dampf.v:.6f} m³/kg") print(f"Spezifische Enthalpie: {dampf.h:.2f} kJ/kg") print(f"Spezifische Entropie: {dampf.s:.4f} kJ/(kgK)") print(f"Phase: {dampf.phase}") Ausgabe: Spezifisches Volumen: 0.151208 m³/kg Spezifische Enthalpie: 3248.23 kJ/kg Spezifische Entropie: 7.1290 kJ/(kgK) Phase: Gas == TESPy == Ein anderes Modul für einen anderen Aufgabenzweck ist TESPy ('''T'''hermal '''E'''ngineering '''S'''ystems in '''Py'''thon). Dieses Modul ist für die Anlagensimulation zuständig. Für nähere Informationen siehe [https://tespy.readthedocs.io/en/main/getting_started/introduction.html]. Als Beipiel sei hier vorerst Code, der von der Google KI generiert wurde, angeführt. Der Code wurde überarbeitet, damit keine Warnungen auftreten. Bitte aber den Code trotzdem mit Vorsicht genießen, auch KI-generierter Code kann Fehler aufweisen. Eine Pumpe wird berechnet: from tespy.components import Sink, Source, Pump from tespy.connections import Connection from tespy.networks import Network # 1. Netzwerk definieren (Zentrales Steuerungselement) # Wir wählen Wasser als Fluid und bar/Celsius als Einheiten nw = Network(fluids=["water"]) nw.units.set_defaults(pressure="bar", pressure_difference="bar", temperature="°C", enthalpy="kJ / kg") # 2. Komponenten erstellen eingang = Source("Wasserquelle") ausgang = Sink("Wasserspeicher") pumpe = Pump("Speisewasserpumpe") # 3. Verbindungen definieren (Komponenten miteinander verknüpfen) c1 = Connection(eingang, "out1", pumpe, "in1") c2 = Connection(pumpe, "out1", ausgang, "in1") # Verbindungen dem Netzwerk hinzufügen nw.add_conns(c1, c2) # 4. Randbedingungen und Parameter festlegen # Zustand am Eingang (Druck, Temperatur, Massenstrom, Fluid-Zusammensetzung) c1.set_attr( v=1, # Massenstrom: 1 kg/s T=20, # Temperatur: 20 °C p=1, # Druck: 1 bar fluid={"water": 1}, # 100% Wasser ) # Zustand am Ausgang / Zielwerte der Pumpe c2.set_attr(p=10) # Ziel-Druck nach der Pumpe: 10 bar # Pumpeneigenschaften festlegen pumpe.set_attr(eta_s=0.8) # Isentroper Wirkungsgrad von 80% # 5. Simulation ausführen nw.solve(mode="design") # 6. Ergebnisse ausgeben nw.print_results() # Spezifische Werte direkt auslesen print("\n--- Auswertung ---") print(f"Erforderliche Pumpenleistung: {pumpe.P.val / 1000:.2f} kW") print(f"Temperatur nach der Pumpe: {c2.T.val:.2f} °C") Ausgabe (gekürzt): iter | residual | progress | massflow | pressure | enthalpy | fluid | component -------+------------+------------+------------+------------+------------+------------+------------ 1 | 7.04e+04 | 12 % | 9.96e+02 | 0.00e+00 | 8.81e+04 | 0.00e+00 | 0.00e+00 2 | 5.91e-12 | 100 % | 1.11e-13 | 0.00e+00 | 7.39e-12 | 0.00e+00 | 0.00e+00 3 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 4 | 5.80e-12 | 100 % | 0.00e+00 | 0.00e+00 | 7.25e-12 | 0.00e+00 | 0.00e+00 Total iterations: 4, Calculation time: 0.01 s, Iterations per second: 480.85 ##### RESULTS (Pump) ##### +-------------------+----------+----------+-----------+----------+----------+----------+ | | P | pr | dp | eta | eta_s | head | |-------------------+----------+----------+-----------+----------+----------+----------| | Speisewasserpumpe | 1.12e+06 | 1.00e+01 | -9.00e+00 | 8.00e-01 | 8.00e-01 | 9.19e+01 | +-------------------+----------+----------+-----------+----------+----------+----------+ ... ... --- Auswertung --- Erforderliche Pumpenleistung: 1124.77 kW Temperatur nach der Pumpe: 20.07 °C = Stochastik = Die {{W|Stochastik}} ist ein sehr weites Feld. Hier werden etliche wichtige Themen kurz angerissen. Python stellt mit den Moduln math und statistics Software zu diesem Zwecke bereit. math und statistics sind bereits im Lieferumfang von Python enthalten. Aber auch mit den externen Modulen NumPy, SciPy, stochastic und pandas kann man Stochastik in Python betreiben. Die Theorie der Wahrscheinlichkeitsrechnung und Statistik soll etwas später in Band 5 dieser Buchreihe behandelt werden. == Lageparameter == import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] m1 = statistics.mean(werte) m2 = statistics.mode(werte) m3 = statistics.median(werte) print("Arithmetischer Mittelwert = ", m1) print("Modalwert = ", m2) print("Median = ", m3) Ausgabe: Arithmetischer Mittelwert = 3.5 Modalwert = 1 Median = 3.0 == Streuungsparameter == Beispiel (Berechnung der Standardabweichung): import statistics werte = [1, 3, 4, 4, 1, 7, 9, 1, 2, 3] s = statistics.stdev(werte) print("Standardabweichung = ", s) Ausgabe: Standardabweichung = 2.6770630673681683 Beispiel (Berechnung des Variationskoeffizienten V = Standardabweichung/Mittelwert) import numpy as np from scipy import stats import statistics k = 50 dat1 = [14, 21, 18, 25, 30, 17, 20] dat = np.array(dat1) # Mit SciPy v = stats.variation(dat) vddof = stats.variation(dat, ddof=1) print("V SciPy: ", v) print("V DDOF SciPy: ", vddof) print(k*"-") # mit NumPy mittelwert1 = np.mean(dat) std_abw1 = np.std(dat) std_abw1ddof = np.std(dat, ddof=1) v1= std_abw1 / mittelwert1 v1ddof = std_abw1ddof / mittelwert1 print("Mittelwert NumPy: ", mittelwert1) print("Std.abw. NumPy: ", std_abw1) print("Std.abw. DDOF NumPy: ", std_abw1ddof) print("V NumPy: ", v1) print("V DDOF NumPy: ", v1ddof) print(k*"-") # nur mit reinem Python mittelwert2 = statistics.mean(dat1) std_abw2 = statistics.stdev(dat1) v2 = std_abw2 / mittelwert2 print("Mittelwert Python: ", mittelwert2) print("Std.abw. Python: ", std_abw2) print("V Python:", v2) print(k*"-") Ausgabe: V SciPy: 0.23890355966467272 V DDOF SciPy: 0.25804533701889254 -------------------------------------------------- Mittelwert NumPy: 20.714285714285715 Std.abw. NumPy: 4.948716593053935 Std.abw. DDOF NumPy: 5.3452248382484875 V NumPy: 0.23890355966467272 V DDOF NumPy: 0.2580453370188925 -------------------------------------------------- Mittelwert Python: 20.714285714285715 Std.abw. Python: 5.3452248382484875 V Python: 0.2580453370188925 -------------------------------------------------- Der Unterschied bei der Standardabweichung zwischen reinem Python und den externen Bibliotheken SciPy und NumPy entsteht dadurch, dass einmal durch (n-1) und das andere Mal nur durch n dividiert wird. Dies kann bei NumPy und SciPy dadurch entschärft werden, indem <code>ddof=1</code> gesetzt wird. ddof steht für '''D'''elta '''D'''egrees '''o'''f '''F'''reedom. == Kombinatorik == Beispiel: import math n = 7 k = 5 print("n! = ", math.factorial(n)) print("Kombinationen (n über k) = ", math.comb(n, k)) Ausgabe: n! = 5040 Kombinationen (n über k) = 21 Siehe zu diesem Thema auch [[Ing Mathematik: Permutationen, Kombinationen, binomischer Lehrsatz]]. Die Anzahlen lassen sich einfach aus den dortigen Formeln ermitteln, z.B. bei Permutationen mit <math>n!</math> oder Variationen mit Wiederholungen als <math>n^k</math>. Will man die Kombinationen oder Variationen aber auch als Liste ausgeben, so kann das Modul <code>itertools</code> nützlich sein. Beispiel (Variationen ohne Wiederholung): from itertools import permutations menge = ["A", "B", "C", "D"] # n = 4 k = 3 variationen = list(permutations(menge, k)) for v in variationen: print("".join(v)) print(50*"-") print(len(variationen)) Ausgabe (gekürzt): ABC ABD ACB ... DCA DCB -------------------------------------------------- 24 Siehe zum Modul <code>itertools</code> auch die Website [https://docs.python.org/3/library/itertools.html]. * Variationen mit Wiederholung: <code>itertools.product()</code> * Kombinationen ohne Wiederholung: <code>itertools.combinations()</code> * Kombinationen mit Wiederholung: <code>itertools.combinations_with_replacement()</code> == Zufallszahlen == Beispiel: import random # Ganzzahlige Zufallszahl von 1 bis 10 zufallszahl1 = random.randint(1, 10) # Gleitpunktzahlen # zwischen 0.0 und 1.0 zufallszahl2 = random.random() # Zahl zwischen 1.5 und 9.5 zufallszahl3 = random.uniform(1.5, 9.5) # aus Liste auswählen farbe = ["Rot", "Grün", "Blau"] zufallswert = random.choice(farbe) print(zufallszahl1) print(zufallszahl2) print(zufallszahl3) print(zufallswert) Ausgabe, z.B.: 5 0.14147945849015753 6.894003397570905 Rot Benötigt man mehrere Zufallszahlen, so ist das Modul <code>numpy</code> zu bevorzugen, z.B.: * Normalverteilung: <code>np.random.normal(...)</code> * Gleichverteilung: <code>np.random.uniform(...)</code> == Histogramm == Zum Thema Histogramm siehe {{W|Histogramm}}. Beispiel (mit Matplotlib): import matplotlib.pyplot as plt import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) plt.hist(daten, bins=25, edgecolor='darkgray') plt.show() Ausgabe: [[Datei:IngMath_histogramm.svg|300px]] Beispiel (mit Seaborn): import matplotlib.pyplot as plt import seaborn as sns import numpy as np daten = np.random.normal(loc=50, scale=10, size=1000) sns.set_theme(style="darkgrid") sns.histplot(data=daten) plt.show() Ausgabe: [[Datei:IngMath_histogramm2.svg|300px]] Das Kürzel <code>sns</code> ist Konvention und steht für die fiktive Figur '''S'''amuel '''N'''orman '''S'''eaborn aus der US-Fernsehserie {{W|The West Wing – Im Zentrum der Macht | The West Wing}}. == Box-Plot == [[File:Elements of a boxplot.svg|400px]] Siehe auch {{W|Box-Plot}}. Beispiel (mit Seaborn erstellt): import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("tips") sns.boxplot(data=df, x="day", y="tip", hue="day", legend=False) plt.show() Ausgabe: [[Datei:IngMath_boxplot.svg|400px]] Beispiel (mit Matplotlib erstellt): import matplotlib.pyplot as plt daten = [12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25] plt.boxplot(daten, patch_artist=True) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() Ausgabe: [[Datei:IngMath_boxplot2.svg|300px]] Um mehrere Box-Plots unterschiedlicher Farbe mit Matplotlib in einem Diagramm zu zeichnen, können Sie folgendermaßen vorgehen: import matplotlib.pyplot as plt daten = [[12, 15, 18, 19, 22, 25, 28, 30, 31, 35, 42, 55, 12, 25], [10, 19, 20, 21, 20, 30, 19, 40, 11, 17, 19, 21]] farben = ["green", "blue"] boxplot = plt.boxplot(daten, patch_artist=True) for patch, farbe in zip(boxplot['boxes'], farben): patch.set_facecolor(farbe) plt.title("Boxplot mit Matplotlib") plt.ylabel("Daten") plt.show() == Regressionsrechnung == Beispiel: import numpy as np import matplotlib.pyplot as plt # Messpunkte x = np.array([1, 3, 5, 6, 8, 10, 20]) y = np.array([3, 4, 5, 5, 7, 9, 11]) # Regressionskurve (Grad 1 = lineare Regression, 2 = Polynom-Regression 2. Gr.) # y = kx + d k, d = np.polyfit(x, y, deg=1) # y = ax**2 + bx + c a, b, c = np.polyfit(x, y, deg=2) x_l = np.linspace(1, 20, 100) y_p = a * x_l**2 + b * x_l + c # Zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.plot(x, k*x + d, color='blue', label='Regressionsgerade') plt.plot(x_l, y_p, color='red', label='Regressionspolynom 2. Gr.') plt.xlabel('x') plt.ylabel('y') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: [[Datei:IngMath_regression.svg|400px]] == Korrelationsrechnung == Beispiel: import pandas as pd import matplotlib.pyplot as plt # Messdaten x = [1, 3, 4, 5, 6] y = [2, 4, 6, 8, 5] daten = {'X': x, 'Y': y} df = pd.DataFrame(daten) # Korrelation korr = df['X'].corr(df['Y']) print(f"Korrelationskoeff.: {korr}") # Messpunkte zeichnen plt.scatter(x, y, color='green', label='Messpunkte') plt.grid() plt.axis("equal") plt.legend(loc="best") plt.show() Ausgabe: Korrelationskoeff.: 0.7556096518348252 [[Datei:IngMath_korrelation.svg|300px]] == Mengen und Venn-Diagramme == Beispiel: import matplotlib.pyplot as plt from matplotlib_venn import venn2 menge_a = {1, 2, 3, 4, 5, 6} menge_b = {4, 5, 6, 7, 8} vereinigung = menge_a | menge_b schnitt = menge_a & menge_b print("Vereinigungsmenge = ", vereinigung) print("Schnittmenge = ", schnitt) venn2([menge_a, menge_b], set_labels=('Menge A', 'Menge B')) plt.show() Ausgabe: Vereinigungsmenge = {1, 2, 3, 4, 5, 6, 7, 8} Schnittmenge = {4, 5, 6} [[Datei:IngMath_venn.svg|300px]] Siehe auch {{W|Mengendiagramm#Venn-Diagramme}}. == Verteilungs- und Dichtefunktion == * CDF ... '''C'''umulative '''D'''istribution '''F'''unction, Verteilungsfunktion * PDF ... '''P'''robability '''D'''ensity '''F'''unction, Dichtefunktion Beispiel (Normalverteilung): import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm my, sigma = 0, 1 x = np.linspace(-4, 4, 50) pdf = norm.pdf(x, my, sigma) cdf = norm.cdf(x, my, sigma) plt.plot(x, pdf, lw=2, label="Dichtefunktion") plt.plot(x, cdf, lw=2, label="Verteilungsfunktion") plt.legend() plt.grid() plt.show() Ausgabe: [[Datei:IngMath_cdf_pdf.svg|300px]] Beispiel (<math>\chi^2</math>-Verteilung): import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats x = np.linspace(0, 20, 500) # df ... degree of freedom, Freiheitsgrad pdf = (stats.chi2.pdf(x, df=2), stats.chi2.pdf(x, df=5), stats.chi2.pdf(x, df=10)) for i in range(0,3): if(i==0): lab = "Freiheitsgrad 2" elif(i==1): lab = "Freiheitsgrad 5" else: lab = "Freiheitsgrad 10" plt.plot(x, pdf[i], label=lab, lw=2) plt.grid() plt.legend() plt.show() Ausgabe: [[Datei:IngMath_chi2.svg | 300px]] == Schätzen und Testen == === Intervallschätzung === Als Beispiel seien Daten gegeben, die von ''Dürr, Mayer: Wahrscheinlichkeitsrechnung und Schließende Statistik; 7. Aufl., Hanser, 2014, Seite 137'' stammen. Und zwar soll das 95%-Vertrauensintervall für den Mittelwert des Kaloriengehalts (kcal/100g) von Hähnchen ermittelt werden. Wir wollen das mit Python inkl. NumPy und SciPy durchführen. Die Stichprobe ist groß (50 Hähnchen): Python-Code: import numpy as np import scipy.stats as stats # Stichprobe daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203] # Parameter definieren konfidenzniveau = 0.95 mean = np.mean(daten) std = np.std(daten, ddof=1) stdfehler = stats.sem(daten) intervall = stats.norm.interval(confidence=konfidenzniveau, loc=mean, scale=stdfehler) print(f"Mittelwert: {mean}") print(f"Standardabweichung: {std}") print(f"Konfidenzintervall: {intervall}") Ausgabe: Mittelwert: 215.48 Standardabweichung: 33.14238915925757 Konfidenzintervall: (np.float64(206.29356722321992), np.float64(224.66643277678006)) Diese Werte stimmen gerundet mit denen im genannten Buch überein. Zum Code selbst: * sem steht für '''s'''tandard '''e'''rror of the '''m'''ean. * <code>scipy.stats.norm</code> ... Modul für die Normalverteilung. === Punktschätzung === Gleiche Daten wie oben bei der Intervallschätzung. Python-Code: import numpy as np from scipy import stats daten = [309, 202, 234, 252, 240, 225, 241, 212, 118, 191, 236, 204, 213, 220, 219, 218, 195, 159, 195, 206, 207, 232, 215, 210, 204, 332, 241, 225, 235, 193, 238, 187, 189, 203, 190, 252, 227, 212, 180, 178, 242, 236, 174, 240, 195, 223, 213, 209, 200, 203 ] mu_hat, sigma_hat = stats.norm.fit(daten) print(f"Schätzer für den Erwartungswert (μ): {mu_hat:.4f}") print(f"Schätzer für die Standardabweichung (σ): {sigma_hat:.4f}") Ausgabe: Schätzer für den Erwartungswert (μ): 215.4800 Schätzer für die Standardabweichung (σ): 32.8093 === Hypothesentests === Beispiel: import numpy as np import scipy.stats as stats x_quer = 12.075 # Stichproben-Mittelwert var = 0.069 # Stichproben-Varianz n = 90 # Stichprobengröße my_0 = 12.0 # Nullhypothese alpha = 0.05 # Signifikanzniveau z_stat = (x_quer - my_0) / np.sqrt(var / n) p_val = 2 * (1 - stats.norm.cdf(np.abs(z_stat))) print(f"Z-Statistik: {z_stat:.4f}") if p_val < alpha: print(f"p-Wert: {p_val:.6f} < alpha:", alpha) print("Die Nullhypothese wird verworfen.") else: print(f"p-Wert: {p_val:.6f} > alpha:", alpha) print("Die Nullhypothese wird nicht verworfen.") Ausgabe: Z-Statistik: 2.7087 p-Wert: 0.006755 < alpha: 0.05 Die Nullhypothese wird verworfen. == Statistische Qualitätskontrolle == Beispiel (Mittelwertkarte): import numpy as np import matplotlib.pyplot as plt # Gegeben sollwert = 50.0 varianz = 4.0 stichproben_umfang = 1 daten = [49.5, 50.2, 53.0, 48.1, 52.6, 53.4, 49.8] # Berechnung standardabweichung = np.sqrt(varianz) streuung = standardabweichung / np.sqrt(stichproben_umfang) cl = sollwert ucl = cl + 3 * streuung lcl = cl - 3 * streuung # Darstellung plt.plot(daten, marker='o', linestyle='-', color='b', label='Messdaten') plt.axhline(cl, color='green', linestyle='-', label=f'CL: {cl}') plt.axhline(ucl, color='red', linestyle='--', label=f'UCL: {ucl:.2f}') plt.axhline(lcl, color='red', linestyle='--', label=f'LCL: {lcl:.2f}') plt.title('Mittelwertkarte') plt.xlabel('Stichprobe') plt.ylabel('Wert') plt.legend(loc='lower left') plt.grid(True) plt.show() Ausgabe: [[Datei:IngMath_mittelwertkarte.svg|300px]] Siehe auch {{W|Shewhart-Regelkarte}} und {{W|Qualitätsregelkarte}}. * UCL ... '''U'''pper '''C'''ontrol '''Limit''', Obere Eingriffsgrenze * LCL ... '''L'''ower '''C'''ontrol '''Limit''', Untere Eingriffsgrenze * CL ... '''C'''enter '''L'''ine, Mittellinie = Ein- und Ausgabe = == print == Die Anweisung print haben wir schon oft verwendet. Hier soll anhand von Beispielen kurz beschrieben werden, was der Befehl print leisten kann. print("Hallo", "Welt", 1, sep="-") print("Hallo", end=" ") print("Welt") Ausgabe: Hallo-Welt-1 Hallo Welt == input == a = int(input("Zahl 1: ")) b = int(input("Zahl 2: ")) print("a + b = ", a+b) Ausgabe (nach Eingabe der beiden Ganzzahlen): Zahl 1: 4 Zahl 2: 5 a + b = 9 == Aus Dateien lesen == Es seinen die datei.txt Hallo Welt. Wie geht es dir? ... und test1.py dat = open("datei.txt", mode = "r") print(dat.read()) dat.close() Ausgabe Hallo Welt. Wie geht es dir? ... Mit dem open()-Befehl wird die Datei datei.txt im Lesemodus geöffnet (r ... read). Mit dem read()-Befehl wird die Datei eingelesen und mittels print ausgegeben. == In Dateien schreiben == dat = open("datei.txt", mode = "a", encoding = "utf-8") dat.write("Hänge Zeile an\n") dat.close() Die Datei datei.txt sieht nach Abarbeitung des obigen Skripts nun so aus Hallo Welt. Wie geht es dir? ... Hänge Zeile an Es wird die Datei im Schreibmodus geöffnet (a ... append (anhängend), w ... write (überschreibend)). write() fügt hier also eine Zeile Text am Dateiende ein. close() schließt die Datei wieder. Das close() kann man sich mit der with-Anweisung auch sparen. with open("datei.txt", mode="a", encoding="utf-8") as dat: dat.write("Hänge Zeile an\n") = Benutzeroberflächen erstellen = == tkinter == {{Wikipedia | Tkinter}} Python bietet standardmäßig das Modul tkinter zur Programmierung von Benutzeroberflächen. Es müssen also bei der Verwendung von tkinter keine externen Module installiert werden. Hier wird eine (sehr) kurze Einführung in das Erstellen von grafischen Oberflächen mittels tkinter gegeben. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") win.minsize(300, 50) but = tk.Button(win, text = "Push the button") but.pack() win.mainloop() Ausgabe: [[Datei:PythonIng_gui1.jpg]] Ein etwas komplizierteres Beispiel sei nachfolgend gezeigt. Es sollen zwei Strings miteinander verknüpft und ausgegeben werden. import tkinter as tk win = tk.Tk() win.title("Hallo Welt!") def on_button_clicked(): str = ent1.get() + ent2.get() lab2["text"] = str ent1 = tk.Entry(win) ent2 = tk.Entry(win) lab1 = tk.Label(win, text="verknuepfen mit") lab2 = tk.Label(win, text="") but = tk.Button(win, text = "=", command=on_button_clicked) ent1.pack(side="left") lab1.pack(side="left") ent2.pack(side="left") but.pack(side="left") ent2.pack(side="left") lab2.pack(side="left") win.mainloop() Ausgabe (vor der Eingabe der Teilstrings): [[Datei:PythonIng_gui2.jpg]] Ausgabe (nach der Eingabe der Teilstrings und dem Drücken des =-Buttons): [[Datei:PythonIng_gui3.jpg]] == curses == {{Wikipedia | curses}} Mit dem curses-Modul lassen sich u.a. TUIs ('''T'''ext '''U'''ser '''I'''nterfaces) erstellen. Ein sehr einfaches Beispiel zur allgemeinen Funktionsweise wird nachstehend geliefert. import curses stdscr = curses.initscr() curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) stdscr.clear() stdscr.addstr("Hallo Welt", curses.color_pair(1)) stdscr.refresh() stdscr.getch() curses.endwin() Als Ausgabe sollte <span style="color:#FF0000;">Hallo Welt</span> (rote Schrift auf weißem Hintergrund) auf dem Terminal/der Konsole erscheinen. Getestet wurde dies mit openSUSE Tumbleweed, Python-Version 3.13.12. Das entsprechende Python-curses-Package muss installiert sein. Allgemeine Informationen zur Terminal-/Konsolengröße und Cursorposition liefert folgendes Programm: import curses stdscr = curses.initscr() stdscr.addstr(3, 5, "LINES: %d" % curses.LINES) stdscr.addstr(4, 5, "COLS: %d" % curses.COLS) (y,x) = stdscr.getyx() stdscr.addstr(5, 5, "Momentane Cursorposition: [%d, %d]" % (y, x)) (y,x) = stdscr.getbegyx() stdscr.addstr(6, 5, "Koordinatenursprung: [%d, %d]" % (y, x)) (y,x) = stdscr.getmaxyx() stdscr.addstr(7, 5, "Fenstergröße: [%d, %d]" % (y, x)) stdscr.addstr(11, 2, "Taste drücken -> Ende") stdscr.refresh() stdscr.getch() curses.endwin() Es sollte sich in etwa folgende Ausgabe ergeben: LINES: 44 COLS: 110 Momentane Cursorposition: [4, 15] Koordinatenursprung: [0, 0] Fenstergröße: [44, 110] Taste drücken -> Ende Zur Funktionsweise von curses siehe auch das Wikibook [[ncurses]]. Zum Verständnis sind dort allerdings elementare Kenntnisse in der Programmiersprache C erforderlich. == Qt == {{Wikipedia | Qt (Bibliothek)}} Auch für das Qt-Framework gibt es eine Anbindung an Python. Nachfolgend ein einfaches Beispiel. import sys from PySide6.QtWidgets import QApplication, QLabel app = QApplication(sys.argv) label = QLabel("Hallo Welt!") label.show() sys.exit(app.exec()) Ausgabe: [[Datei:PythonIng_gui10.png]] == Gtk == {{Wikipedia | GTK (Programmbibliothek)}} Eine idente Ausgabe, wie oben für Qt gezeigt, erzeugt z.B. folgendes Gtk-Programm: import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk def on_activate(app): win = Gtk.ApplicationWindow(application=app) lab = Gtk.Label(label="Hallo Welt!") win.set_child(lab) win.present() app = Gtk.Application() app.connect('activate', on_activate) app.run(None) Auch für die Benutzung von Qt und Gtk müssen die jeweiligen Packages installiert sein. Getestet wurden die entsprechenden Python-Programme nur unter openSUSE Tumbleweed. Wie das GTK-Paket unter MS Windows 11 installiert wird, siehe z.B. [https://www.gtk.org/docs/installations/windows Setting up GTK for Windows]. Damit sei aber das Thema "Benutzeroberflächen erstellen" hier abgeschlossen, da dies schon ein sehr spezielles Aufgabengebiet ist, das eher Informatiker und nicht so sehr Ingenieure anspricht. Bei Bedarf siehe aber ggf. die entsprechenden Links unten in diesem Tutorial. Dort sind weiterführende Informationen zu finden. = Style Guide, flake8, pylint, Black etc. = == Style Guide == Wie man schönen und richtigen Python-Code schreibt, erfahren Sie in * [https://peps.python.org/pep-0008/ PEP 8 – Style Guide for Python Code] == Formatter und Linter == Ein Modul, das prüft, ob die Richtlinien im Style Guide eingehalten wurden, ist ''flake8'': * [https://flake8.pycqa.org/en/latest/ Flake8: Your Tool For Style Guide Enforcement] Code formatieren kann man auch mit [https://pypi.org/project/black/ Black]. Z.B. übersetzt <code>black test1.py</code> die Datei <code>test1.py</code> import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) in import sympy as sp H = sp.Symbol("H", positive=True) T = sp.Symbol("T", positive=True) t = sp.Symbol("t") f = sp.Piecewise( (H, (t > 0) & (t < T / 2)), (2 * H / T * (t - T / 2), (t > T / 2) & (t < T)) ) f_series = sp.fourier_series(f, (t, 0, T)) sp.pprint(f_series.truncate(4)) Die Programmausgabe ist reformatted test1.py All done! ✨ 🍰 ✨ 1 file reformatted. Der Unterschied zwischen Black und Flake8: * Black ist ein Code-Formatter. Er formatiert Ihren Code um, sodass er im Einklang mit PEP 8 steht. * Flake8 ist ein {{W|Lint (Programmierwerkzeug) | Code-Linter}}. Flake8 verändert Ihren Code nicht, sondern durchsucht ihn nach potenziellen Fehlern etc. Am obigen Beispiel sieht man auch, dass flake8 und Black nicht immer einer Meinung sind. Flake8 (<code>flake8 test1.py</code>) würde standardmäßig den mit Black formatierten Code bemängeln: test1.py:8:80: E501 line too long (80 > 79 characters) Diese Diskrepanz kann beseitigt werden. Da 79 Zeichen auf modernen Bildschirmen meist als zu kurz empfunden werden, ist ein Limit von 88 Zeichen (Black-Standard) oder mehr empfehlenswert. Um dies zu implementieren, erstellen Sie in Ihrem Projektverzeichnis eine <code>.flake8</code>-Datei mit dem Inhalt [flake8] max-line-length = 88 Und schon ignoriert Flake8 dieses Problem. Ein anderer Linter ist pylint. Der würde beim Abarbeiten des obigen Beispiels, z.B. mit <code>pylint test1.py</code> noch eine Kleinigkeit bemängeln: ************* Module test1 /home/hr/tmp/test1.py:1:0: C0114: Missing module docstring (missing-module-docstring) ------------------------------------------------------------------ Your code has been rated at 8.57/10 (previous run: 8.57/10, +0.00) Auch pylint muss vor der ersten Verwendung installiert werden (z.B. mittels pip, virtuelle Umgebung, YaST). Die Dokumentation zu pylint findet sich auf [https://pylint.readthedocs.io/en/latest/]. <u>Aufgabe:</u> Fügen Sie einen "module docstring" in die <code>test1.py</code>-Datei ein und testen Sie erneut mit flake8, Black und pylint. <small>Sehen Sie zum Thema docstrings auch [https://peps.python.org/pep-0257/#what-is-a-docstring PEP 257 – Docstring Conventions].</small> Es gibt noch weitere Formatierungswerkzeuge für Python-Code. Z.B. [https://docs.astral.sh/ruff/ Ruff], ein moderner Code-Formatter und -Linter. Mittels <code>ruff check test1.py</code> würde obiger Code geprüft (Linter). <code>ruff format test1.py</code> formatiert den Code (Formatter). == Type Checker == "Type Checker" sind z.B. * mypy * pyright * ty Diese prüfen die Datentypen, z.B. in folgendem Code def greetings(name: str) -> str: return "Hello, %s" % name print(greetings(42)) Python selbst, flake8, ruff oder black würden diesen Code ohne zu Murren akzeptieren. "Type Checker" würden aber sehr wohl Alarm schlagen, z.B. liefert <code>mypy</code> folgende Ausgabe test1.py:5: error: Argument 1 to "greetings" has incompatible type "int"; expected "str" [arg-type] Found 1 error in 1 file (checked 1 source file) == Sonstige Tools == Andere Tools für die {{W|Statische Code-Analyse|statische Codeanalyse}}, die aber für Ingenieure weniger interessant sein dürften, sind z.B. * Radon: Liefert verschiedene {{W|Softwaremetrik|Codemetriken}} (Komplexität, Wartbarkeitsindex ...) * Bandit: Findet Sicherheitslücken Tools für die {{W|Dynamisches Software-Testverfahren|dynamische Codeanalyse}}, z.B.: * DynaPyt (Framework zur dynamischen Programmanalyse) * cProfile (Profiler) * Memory Profiler (Speicheranalyse) * Memray (Speicheranalyse) * tracemalloc (Speicheranalyse) Paket- und Projektmanagement (pip-Ersatz etc.): * uv * Poetry * Conda * pipx Packaging-Tools (Freezer) und {{W|Compiler#Sonderformen|Transpiler}} : * pyinstaller ** erstellt eigenständige, ausführbare Binärdatei ** kein Cross-Compiler ** kein Schutz vor Reverse-Engineering ** langsam ** packt alles in eine Datei ** sehr große Datei ** Befehl, z.B.: <code>pyinstaller --onefile test1.py</code> ** GUI: <code>auto-py-to-exe</code> * cx_Freeze * nuitka ** Übersetzt Python-Code in C/C++-Code und weiter in eine ausführbare Datei ** kein Cross-Compiler ** Schutz vor Reverse-Engineering ** Befehl, z.B.: <code>nuitka --standalone --onefile test1.py</code> * cython = Einige Integrierte Entwicklungsumgebungen (IDEs)= Werden Programmtexte größer und umfangreicher, so ist das Arbeiten mit der interaktiven Programmierumgebung bzw. das direkte Ausführen von Python-Skripten mühsam. Man wünscht sich z.B. Hilfen zum Debuggen oder die automatische Code-Vervollständigung. Zu diesem Zweck wurden IDEs (Integrated Development Environments) geschaffen. Von diesen seinen nachfolgend auszugsweise einige kurz beschrieben. Testen Sie einfach aus, welche davon für Sie bzw. für Ihr Python-Projekt geeignet sind. == IDLE == IDLE ist die mit dem Python-Programmpaket mitgelieferte IDE. Der Name leitet sich einerseits ab vom Monty-Python-Mitglied Eric Idle, andererseits steht es als Abkürzung für "'''I'''ntegrated '''D'''evelopment and '''L'''earning '''E'''nvironment. IDLE ist einfach zu bedienen, bietet aber schon einen beachtlichen Leistungsumfang. Nachfolgend wird ein Screenshot zu IDLE geliefert. Rechts ist das Editor-Fenster zu sehen, links die interaktive Programmierumgebung. Um das Beispiel selbst nachvollziehen zu können, starten Sie IDLE. Das geht ähnlich, wie Sie die interaktive Programmierumgebung von Python starten (nur, dass Sie eben das IDLE-Icon doppelklicken und nicht das Python-Icon. Unter Linux geben Sie einfach in einem Terminal <code>idle3.13</code> o. Ä. ein). Weiter geht es mit "File - Open - ...". Die auszuführende Datei auswählen (im konkreten Fall ein "Hallo-Welt"-Programm). Es erscheint das rechte Fenster. Dort "Run - Run Module" auswählen. Und schon wird im linken Fenster "Hallo Welt!" ausgegeben. [[Datei:PythonIng_idle1.jpg | 600px]] Siehe auch {{W|IDLE}}. == PyCharm == PyCharm ist ein kommerzielles Produkt. Es gab aber auch eine kostenlose Community Edition. Seit 2025 sind beide Varianten vereint. Für die ersten 30 Tage sind die Pro-Funktionen frei verfügbar, danach nur noch die Kernfunktionalitäten (oder man bezieht kostenpflichtig die Pro-Version). Zu beziehen ist PyCharm unter dem Weblink [https://www.jetbrains.com/pycharm/]. Nachfolgend ein etwas abgewandeltes "Hallo Welt"-Programm, editiert und ausgeführt mit PyCharm. [[Datei:PyCharm_IDE_2023_screenshot.png | 600px]] Siehe auch {{W|PyCharm}}. == Eric == Auch eric ist Open Source und steht unter der GNU General Public License Version 3 oder später. Zu beziehen ist diese Software unter [https://eric-ide.python-projects.org/]. [[Datei:Screenshot_Eric_4.png | 600px]] Siehe auch {{W|eric (Software)}}. <small> Unter openSUSE Tumbleweed sollte sich eric auch mit YaST installieren lassen. Bei mir gibt es aber dann beim Ausführen des eric-Programms eine Fehlermeldung (Stand März 2026): ... ModuleNotFoundError: No module named 'PyQt6.QtPdfWidgets' Umgehen kann man dieses Problem aber wieder mit dem Erstellen einer virtuellen Umgebung, in etwa so python3.13 -m venv ~/tmp/venv1 cd ~/tmp/venv1/bin ./python3.13 -m pip install --upgrade --prefer-binary eric-ide ./eric7_ide </small> == PyScripter == Vom Funktionsumfang vergleichbar mit den vorherigen IDEs ist PyScripter. Auch PyScripter ist Open Source. Die Projekt-Homepage findet sich auf [https://sourceforge.net/projects/pyscripter/]. PyScripter ist nur für MS Windows verfügbar. [[Datei:PythonIng_pyscripter1.jpg | 600px]] == Spyder IDE == Spyder enthält bereits eine stabile Python-Version und etliche Module (z.B. matplotlib, numpy, control). Ansonsten kann dieses Softwarepaket vom Funktionsumfang her mit den anderen genannten IDEs locker mithalten. Spyder wurde unter der MIT-Lizenz veröffentlicht. Diese Software findet sich auf [https://www.spyder-ide.org]. [[Datei:Spyder-windows-screenshot.png | 600px]] Siehe auch {{W|Spyder (Software)}} == Eclipse IDE== Die {{W|Eclipse_(IDE)|Eclipse-IDE}} kann für Python aufgerüstet werden. Dazu gibt es das PyDev-Plugin. Installiert wird es über * Help > Eclipse Marketplace... * Find - PyDev - Install Danach muss noch der Pfad zum Python-Interpreter festgelegt werden * Window > Preferences > PyDev > Interpreters > Python Interpreter > New ... Das Ergebnis ist ähnlich wie im folgenden Bild, nur dass statt C/C++ Python Verwendung findet. [[Datei:Setting Up Eclipse CDT helloout.png | 600px]] == Sonstige == Die genannten IDEs sind nicht die Einzigen. Es gibt, um dem Image Pythons als beliebteste Programmiersprache gerecht zu werden, noch einige andere. Sowohl Open Source-Programme als auch kommerzielle Programme sind im Web zu finden, z.B. Thonny oder {{W|Visual Studio Code}}. Unter Linux kann man auch {{W|KDevelop}}, ausgestattet mit dem Python3-Plugin, einsetzen. Braucht man den Umfang von ausgewachsenen IDEs nicht, so kann man auch normale Texteditoren verwenden (z.B. {{W|Geany}} oder {{W|Kate_(Texteditor)|Kate}}). = Debuggen und Testen = Das Debuggen und Testen von Programmen sind wichtige Bestandteile der Programmierung. Syntaxfehler lassen sich i.A. leicht beheben. Schwieriger ist das Eingrenzen von logischen Fehlern, die ev. nur in bestimmten Situationen auftreten und keine explizite Fehlermeldung hervorrufen. Das Programm liefert falsche Ergebnisse oder es stürzt aus heiterem Himmel ab. Um das zu verhindern gibt es verschiedene Werkzeuge, die bei der Fehlersuche behilflich sein können. Vorerst siehe aber zwecks Begriffsklärung noch folgende Links: * {{W|Debuggen}} * {{W|Debugger}} * {{W|Softwaretest}} <gallery> First Computer Bug, 1947.jpg Test ganzheitlich.png V-Modell.svg </gallery> == Das Modul pdb == Python bringt schon ein Modul zum Debuggen mit. Siehe z.B. [https://docs.python.org/3/library/pdb.html pdb — The Python Debugger]. Komfortabler lässt sich das aber mittels Integrierter Entwicklungsumgebungen (IDEs) angehen. == Debuggen mit IDEs == Für die IDEs IDLE und Spyder sei kurz auf die entsprechenden Webseiten verwiesen: * [https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html Debugging under IDLE]. * [https://docs.spyder-ide.org/current/panes/debugging.html Spyder Debugger] Dort wird die Vorgehensweise auch mittels Screenshots erläutert. == assert == assert ... behaupten, zusichern ({{W|Assertion (Informatik)}}) Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10., 0.) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10., 0.) File "/home/hr/Develop/test1.py", line 4, in print1 assert y != 0.0 ^^^^^^^^ AssertionError Python-Code: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1("10.", "5.") File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Aber auch bei nachfolgendem Code gibt es eine Fehlermeldung: def print1(x, y): assert type(x) == float assert type(y) == float assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 Traceback (most recent call last): File "/home/hr/Develop/test1.py", line 8, in <module> print1(10, 5) File "/home/hr/Develop/test1.py", line 2, in print1 assert type(x) == float ^^^^^^^^^^^^^^^^ AssertionError Damit letzteres funktioniert, kann man den Programmcode so umschreiben: def print1(x, y): assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) print1(10., 5.) print1(10, 5) Ausgabe: 2.0 2.0 Und jetzt fangen wir den <code>AssertionError</code> auf: def print1(x, y): try: assert type(x) == float or type(x) == int assert type(y) == float or type(y) == int assert y != 0.0 print(x/y) except(AssertionError): print("Hallo") print1(10., 5.) print1("10.", "5.") Ausgabe: 2.0 Hallo Ich hoffe, es ist wenigstens im Ansatz klar geworden, wofür <code>assert</code> gut sein kann. Ausschalten kann man die <code>assert</code>-Überprüfung übrigens mit dem Python-Schalter <code>-O</code>. == Doctests == Innerhalb eines Docstrings kann die Arbeit im interaktiven Modus simuliert werden. Nach den Promptzeichen (>>>) erfolgen dann bei unserem Beispiel innerhalb des Docstrings simulierte Aufrufe der Funktion <code>print1()</code>. Danach folgen jeweils die Sollresultate. Wird das Modul oder die Datei als Hauptprogramm aufgerufen, so wird die Funktion <code>doctest.testmode()</code> aufgerufen und ein Bericht auf der Konsole ausgegeben. Wird das Modul nicht als Hauptprogramm aufgerufen, sondern importiert, dann wird diese <code>testmod</code>-Funktion nicht aufgerufen. D.h. dieser Code kann sowohl für Testzwecke als auch für den produktiven Einsatz verwendet werden. Das ist auch sinnvoll, weil wenn man Teile der Datei immer löschen bzw. einfügen müsste, so würden sich Fehlerquellen auftun. Das würde den Sinn und Zweck von Doctests konterkarieren. def print1(x=0., y=1.): """ Dividiere zwei Zahlen Autor: Intruder Datum: 12.08.2025 >>> print1(2., 1.) 2.0 >>> print1(5., 2.) 2.5 >>> print1(5.) 5.0 """ print(x/y) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) Ausgabe: Trying: print1(2., 1.) Expecting: 2.0 ok Trying: print1(5., 2) Expecting: 2.5 ok Trying: print1(5.) Expecting: 5.0 ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.print1 3 tests in 2 items. 3 passed and 0 failed. Test passed. Das gezeigte Beispiel ist so ziemlich das einfachste, das es gibt. Für weiterführende Details siehe z.B.: * [https://peps.python.org/pep-0257/ PEP 257 – Docstring Conventions] * [https://docs.python.org/3/library/doctest.html doctest — Test interactive Python examples] ''Einschub:'' Ganz ähnlich kann man auch Klassen testen, z.B. in folgendem Code-Fragment class Fahrzeug: # siehe Abschnitt "Objektorientierte Programmierung" # ... if __name__ == "__main__": vauweh = Fahrzeug(170, 90) beemweh = Fahrzeug(200, 120) print(vauweh.convertGeschw()) print(beemweh.convertGeschw()) Wird das Script als Hauptprogramm ausgeführt (z.B. zu Testzwecken), so erfolgt die Ausgabe der zwei, via <code>convertGeschw()</code>, umgerechneten Geschwindigkeiten. Wird es aber als Modul eingebunden, so wird der letzte Programmabschnitt nicht ausgeführt (<code>__name__ == "__main__"</code> ergibt <code>False</code>). == pytest == Siehe zu diesem Thema auch {{W|Modultest}}. pytest ist ein externes Modul und muss vorab installiert werden, z.B. mittels pip install -U pytest pip install -U pytest-html Python-Code, Datei test1.py: def add(x, y): return x + y def test_answer(): assert add(1, 1) == 3 Starten von pytest in der Konsole: pytest test1.py Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py F [100%] ========================================================= FAILURES ========================================================== ________________________________________________________ test_answer ________________________________________________________ def test_answer(): > assert add(1, 1) == 3 E assert 2 == 3 E + where 2 = add(1, 1) test1.py:6: AssertionError ================================================== short test summary info ================================================== FAILED test1.py::test_answer - assert 2 == 3 ===================================================== 1 failed in 0.09s ===================================================== Hier erhalten wir einen Fehler, da 1+1 eindeutig ungleich 3 ist. Aber aus irgendeinem Grund wollte der Programmierer oder Tester in diesem Fall, dass dies so ist. Testfälle werden übrigens mit dem Präfix <code>test_</code> eingeleitet. Python-Code: def add(x, y): return x + y + 1 def test_answer(): assert add(1, 1) == 3 Ausgabe: ==================================================== test session starts ==================================================== platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.6.0 rootdir: /home/hr/Develop plugins: anyio-4.10.0, metadata-3.1.1, html-4.1.1 collected 1 item test1.py . [100%] ===================================================== 1 passed in 0.01s ===================================================== Jetzt ist alles in Ordnung. Weiterführendes siehe z.B. * [https://docs.pytest.org/en/stable/ pytest: helps you write better programs] == unittest == Auch unittest dient zur Durchführung von Testreihen, ist aber Bestandteil von Python. Hier wird vorerst nicht näher darauf eingegangen. Siehe z.B. * [https://docs.python.org/3/library/unittest.html unittest — Unit testing framework] Lt. ''Inden: Python Challenge; dpunkt, 2021, Seite 481'' soll unittest weniger komfortabel als pytest sein. Einen Vergleich von unittest mit pytest findet man in * [https://knapsackpro.com/testing_frameworks/difference_between/pytest/vs/unittest pytest vs unittest] = Python und Anwendungsprogramme = Bisher stand immer alleine die Programmiersprache Python (ev. unter Einbeziehung von Modulen) im Mittelpunkt der Betrachtungen. Aber Python kann auch als Makrosprache für Anwendungsprogramme auftreten. Als Beispiele seien {{W|FreeCAD}} und {{W|LibreOffice}} genannt. == FreeCAD == FreeCAD ist ein freies 3D-CAD-Programm. Hier soll nicht auf die Bedienung dieses CAD-Pakets eingegangen werden, sondern nur auf die Möglichkeit, mittels Python Makros zu schreiben. Als einfacher Einstieg soll ein Makro erstellt werden, welches eine Kugel (rot) und einen Quader (blau) zeichnet. Folgende Vorbereitungsschritte sind erforderlich (es sei vorausgesetzt, dass FreeCAD schon am Rechner installiert ist. Downloaden können Sie das Programm von der Website [https://www.freecad.org/downloads.php?lang=en]): * FreeCAD starten * Leere Datei erstellen * Makro > Makros > Erstellen ... Es öffnet sich ein leeres Editorfenster, in das Sie folgenden Code eingeben können: import FreeCAD import Part doc = FreeCAD.newDocument() # Kugel kugel = Part.makeSphere(10) form_element = doc.addObject("Part::Feature", "Kugel") form_element.Shape = kugel kug = FreeCAD.ActiveDocument.getObject("Kugel") kug.ViewObject.ShapeColor = (1.0, 0.0, 0.0) neuePosition = App.Vector(5, 2.5, 2.5) kug.Placement = App.Placement(neuePosition, kug.Placement.Rotation) # Quader quader = Part.makeBox(5, 5, 50) form_element = doc.addObject("Part::Feature", "Quader") form_element.Shape = quader quad = FreeCAD.ActiveDocument.getObject("Quader") quad.ViewObject.ShapeColor = (0.0, 0.0, 1.0) doc.recompute() Diesen Code können Sie nun ausführen: * Makro > Makro ausführen Es ergibt sich folgende Ausgabe (gedreht und gezoomt): [[Datei: PythonIng_freecad1.png|500px]] Siehe auch [https://wiki.freecad.org/Python_scripting_tutorial/de# Anleitung Skripterstellung mit Python]. Getestet wurde obiges Beispiel mit FreeCAD 1.1.0 unter Linux und 1.1.1 unter MS Windows. == LibreOffice == LibreOffice ist ein freies Officepaket ([https://de.libreoffice.org/]). Hier soll nur das Tabellenkalkulationsprogramm Calc kurz betrachtet werden. Es seinen in den ersten 3 Zellen (A1 bis A3) Zahlen gegeben. Diese sollen mit einem Python-Makro addiert und das Resultat in der Zelle A5 ausgegeben werden. Auch hier sind Vorbereitungsarbeiten nötig: * zuerst muss unter Linux das Verzeichnis <code>~/.config/libreoffice/4/user/Scripts/python</code> erstellt werden * für MS Windows ist es das Verzeichnis <code>%APPDATA%\LibreOffice\4\user\Scripts\python</code> ** drücken Sie zuerst Win + R (es öffnet sich das Ausführen-Fenster) ** geben Sie <code>%appdata%</code> ein, danach drücken Sie Enter (es öffnet sich der Explorer) ** navigieren Sie zu dem genannten Verzeichnis bzw. erstellen Sie das Verzeichnis * in diesem Verzeichnis wird dann mit einem beliebigen Texteditor das Python-Makro erstellt, in unserem Fall die Datei <code>summiere_zellen.py</code>: import uno def summiere_zellen(*args): # Zugriff auf das aktuelle Dokument und das aktive Tabellenblatt ctx = uno.getComponentContext() smgr = ctx.getServiceManager() desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) doc = desktop.getCurrentComponent() sheet = doc.getCurrentController().getActiveSheet() # Werte aus den Zellen A1 bis A3 auslesen w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value # Addition der drei Werte summe = w1 + w2 + w3 # Ergebnis in die Zelle A5 schreiben sheet.getCellRangeByName("A5").Value = summe * siehe dazu ggf. auch [https://help.libreoffice.org/latest/de/text/sbasic/python/python_locations.html?DbPAR=BASIC]. Weiter geht es in LibreOffice Calc mit dem Menü ''Extras > Makros > Makros verwalten > Python''. Dort wird das Makro <code>summiere_zellen</code> ausgeführt. Es ergibt sich z.B. folgendes Resultat [[Datei:PythonIng_libreoffice1.png]] Das Kürzel <code>uno</code> steht für '''U'''niversal '''N'''etwork '''O'''bjects. Etwas einfacher geht's auch so: def summiere_zellen(): desktop = XSCRIPTCONTEXT.getDesktop() model = desktop.getCurrentComponent() sheets = model.getSheets() sheet = sheets.getByIndex(0) w1 = sheet.getCellRangeByName("A1").Value w2 = sheet.getCellRangeByName("A2").Value w3 = sheet.getCellRangeByName("A3").Value cell = sheet.getCellRangeByName("A5") cell.setValue(w1 + w2 + w3) Empfohlen wird auch, das Erweiterungspaket APSO ('''A'''lternative '''P'''ython '''S'''cript '''O'''rganizer, apso.oxt) zu installieren. Die Vorgehensweise wird hier nicht gezeigt, sondern nur darauf hingewiesen, dass man das einfach "googeln" kann. Siehe zur Python-Programmierung für LibreOffice auch [https://wiki.documentfoundation.org/Macros/Python_Guide/de Makros/Python-Handbuch]. Getestet wurden obige Beispiele mit LibreOffice 26.2.3.2 unter Linux und 26.2.1.2 unter MS Windows. = Ausblick = Dies war eine kurze Einführung in die Berechnungs- und Darstellungsmöglichkeiten mit Python. Es sollten etliche relevante Themen behandelt, oder zumindest kurz angesprochen worden sein. Wem dieser Text nicht ausreichend ist, der sei auf die entsprechenden weiterführenden Weblinks, Bücher und die Python-Hilfefunktion verwiesen. Python kennt noch viel mehr Befehle, als hier dargestellt wurden. Das Themenspektrum ist auch durch die Einbindung externer Module fast beliebig erweiterbar. = Weblinks= == Python allgemein == * [https://www.python.org/ Python Homepage] == Externe mathematische Module == * [https://numpy.org/ NumPy] * [https://numpy.org/doc/stable/user/numpy-for-matlab-users.html NumPy for MATLAB users] * [https://scipy.org/ SciPy] * [https://www.sympy.org/en/index.html SymPy] * [https://pandas.pydata.org/ pandas] * [https://github.com/maroba/findiff findiff] * [https://mpmath.org/ mpmath] == Externe Module für Grafiken == * [https://matplotlib.org/ Matplotlib] * [https://vpython.org/ VPython] * [https://docs.vtk.org/en/latest/api/python.html VTK] == Erstellung von User Interfaces == * [https://docs.python.org/3/library/tkinter.html tkinter - Python interface to Tcl/Tk] * [https://docs.python.org/3/library/curses.html curses - Terminal handling for character-cell displays] * [https://wiki.qt.io/Qt_for_Python Qt for Python] * [https://www.gtk.org/docs/language-bindings/python GTK and Python] == Erstellen virtueller Umgebungen == * [https://docs.python.org/3/library/venv.html venv - Creation of virtual environments] == Sonstige == * [https://python-control.readthedocs.io/en/stable/ Python Control Systems Library] * [https://pypi.org/project/regex/ regex - Regular Expressions] * [http://pyromat.org/ PYroMat] * [https://coolprop.org/coolprop/wrappers/Python/index.html CoolProp] * [https://pypi.org/project/iapws/ iapws] * [https://tespy.readthedocs.io/en/main/getting_started/introduction.html TESPy - Thermal Engineering Systems in Python] = Bücher = == Gedruckte Bücher, OpenBooks, Magazine == * Diverse: c't Python Lernen, Verstehen, Anwenden; Heise, 2022 * Ernesti, Kaiser: Python3 - das umfassende Handbuch; 5. Aufl., Rheinwerk, [https://openbook.rheinwerk-verlag.de/python/ OpenBook] * Inden: Python Challenge; dpunkt, 2021, ISBN 978-3-86490-809-5 * Klein: Numerisches Python; 2. Aufl., Hanser, 2023, ISBN 978-3-446-47170-2 * Steinkamp: Der Python-Kurs für Ingenieure und Naturwissenschaftler; Rheinwerk, 2021, ISBN 978-3-8362-7316-9 * Weigend: Python 3 - Das umfassende Praxisbuch; 9. Aufl., mitp, 2022, ISBN 978-3-7475-0544-1 * Woyand: Python für Ingenieure und Naturwissenschaftler; 4. Aufl., Hanser, 2021, ISBN 978-3-446-46483-4 == Andere Wikibooks == * [[:en:Subject:Python_programming_language | Englische Wikibooks zum Thema Python]] * [[Python|Deutschsprachiges Python-Wikibook]] [[Bild:2von10.png|20%]] * [[Python unter Linux|Python 2.7 unter Linux]] [[Bild:10von10.png|100%]] {{Navigation_zurückhochvor_buch| zurücktext=Julia für Ingenieure| zurücklink=Ing Mathematik: Julia| hochtext=Gesamtinhaltsverzeichnis| hochlink=Ing:_Mathematik_für_Ingenieure| vortext=Landau-Notation| vorlink=Ing Mathematik: Landau}} bg903de12fstqbb62kvlctzi2j95wmd Traktorenlexikon: AGCO-Allis 9765 0 122891 1088105 2026-06-13T17:09:52Z Baupit 56622 Neue Seite (vgl. [[WB:AZ]]) 1088105 wikitext text/x-wiki {{:Traktorenlexikon: Navigation |HERSTELLER-LINK=Traktorenlexikon: AGCO-Allis |HERSTELLER= AGCO-Allis}} {{:Traktorenlexikon: Modell-Infobox | HERSTELLER = AGCO-ALLIS | MODELLREIHE = 9700 er - SERIE | MODELL = 9765 | BILD = | BILDBESCHREIBUNG = | BAUWEISE = Blockbauweise | PRODUKTIONSBEGINN = 1998 | PRODUKTIONSENDE = 2001 | STÜCKZAHL = | EIGENGEWICHT = 7.802 (4 WD: 8.345) | LÄNGE = 5.156 | BREITE = 2.494 | HÖHE = 3.073 | RADSTAND = 2.858 | BODENFREIHEIT = 445 | SPURWEITE = | SPURWEITE VORNE = (4 WD: 1.522-2.233) | SPURWEITE HINTEN = 1.562-3.299 | WENDERADIUS MIT LENKBREMSE = | WENDERADIUS OHNE LENKBREMSE = | BEREIFUNG VORNE = 11.00-16 ASF (4 WD: 14.9 R 30 AS) | BEREIFUNG HINTEN = 18.4 R 42 AS | LEISTUNG KW = 136,1 | LEISTUNG PS = 185 | NENNDREHZAHL = 2.200 | ZYLINDER = 6 | HUBRAUM = 8.705/8.268 | DREHMOMENTANSTIEG = 27/26 | KRAFTSTOFF = Diesel | KÜHLSYSTEM = Wasserkühlung | ANTRIEBSTYP = Heck- und Allradantrieb | GETRIEBE = 18 V/6 R oder 36 V/12 R | HÖCHSTGESCHWINDIGKEIT = 30 oder 40 | KATEGORIESORTIERUNG = }} Die im Jahr 1998 vorgestellte 9700 er-Serie war die letzte von AGCO gefertigte Baureihe, in der der Markenname "ALLIS" Verwendung fand. Eins der großen Modelle war der AGCO-ALLIS 9765, der eine Nennleistung von 185 DIN-PS aufwies. Anfangs setzte AGCO ein aufgeladenes Sechszylinder-NAVISTAR-Aggregat ein, das später durch ein aufgeladenes und mit Ladeluftkühlung bestückten Sechszylinder-CUMMINS-Aggregat ersetzt wurde. Das serienmäßige Triebwerk verfügte über 18/6-Gänge, was auf Wunsch auf 36/12-Gänge gesteigert werden konnte. Der AGCO-ALLIS 9765 bot dem Fahrer modernen Komfort, darunter eine verbesserte Rundumsicht, eine ergonomische Armlehnensteuerung und eine elektronische Regelung für den Heckkraftheber. Er war sowohl als Hinterrad- als auch als Allradantrieb erhältlich. ==Motor== * INTERNATIONAL-NAVISTAR, Typ: DT-530 , stehender wassergekühlter Viertakt-Sechszylinder-Reihen-Saugmotor mit Direkteinspritzung, hängenden Ventile, Kraftstoffpumpe, Leichtmetall-Kolben, BOSCH-Einspritzpumpe, mechanischer Drehzahlregler, Druckumlaufschmierung mittels Zahnradpumpe, BOSCH-Mehrloch-Einspritzdüsen, zahnradgetriebene Nockenwelle, DONALDSON-Trockenluftfilter, Ölkühler, Turbolader, siebenfach-gelagerte Kurbelwelle, auswechselbare Zylinderlaufbuchsen und Lamellenkühler mit Lüfter. * Bohrung = 116,59 mm, Hub = 135,89 mm * Verdichtungsverhältnis = 16,5:1 * Max. Drehmoment = 869 Nm bei 1.500 U/min. * Drehmomentanstieg = 27 % bei 1.800 U/min. * Mittlere Kolbengeschwindigkeit = 9,97 m/s * Max. Drehmomentanstieg = 46,6 % * Geregelter Drehzahlbereich = 650 bis 2.404 U/min. * Ladedruck = 1,75 bar "Alternativ:" * CUMMINS, Typ: C-8.3 T, stehender wassergekühlter Viertakt-Sechszylinder-Reihen-Saugmotor mit Direkteinspritzung, hängenden Ventile, Leichtmetall-Kolben, FIAMM-Kraftstofffilter, BOSCH-Einspritzpumpe, mechanischer BOSCH-Drehzahlregler, Ölkühler, NELSON-Schalldämpfer, Druckumlaufschmierung mittels Zahnradpumpe, BOSCH-Mehrloch-Einspritzdüsen, zahnradgetriebene Nockenwelle, DONALDSON-Trockenluftfilter incl. Vorfilter, Wastegate-Turbolader incl. Ladeluftkühlung, siebenfach-gelagerte Kurbelwelle, auswechselbare Zylinderlaufbuchsen, Lamellenkühler und Lüfter. * Bohrung = 114 mm, Hub = 135 mm * Verdichtungsverhältnis = 17,3:1 * Max. Drehmoment = 858 Nm bei 1.300 U/min. * Mittlere Kolbengeschwindigkeit = 9,90 m/s * Drehmomentanstieg = 26 % bei 1.800 U/min. * Geregelter Drehzahlbereich = 800 bis 2.480 U/min. * Max. Drehmomentanstieg = 43,7 % * Max. Ladedruck = 1,59 bar ==Kupplung== * Ölgekühlte-nasse Mehrscheibenkupplung ==Getriebe== * Im Ölbad laufendes POWERSHIFT-Getriebe * Die Steuerung der Gänge erfolgt durch hydraulische Kupplungspakete * Gangwechsel erfolgt ohne Betätigung der Kupplung oder Unterbrechung des Kraftflusses * System passt Motordrehzahl und Getriebeübersetzung automatisch an, schaltet bei leichter Last hoch, bei schwerer Last herunter 18 Vorwärts- und 6 Rückwärtsgänge * Optional als Kriechganggetriebe 36 Vorwärts- und 12 Rückwärtsgänge ==Geschwindigkeiten vor- und rückwärts== "Geschwindigkeiten mit Bereifung 18.4 R 42 AS" {| class="wikitable" |- ! bei Motordrehzahl (U/min) !! 2.200 |- | 1.Gang || 2,19 km/h |- | 2.Gang || 2,83 km/h |- | 3.Gang || 3,65 km/h |- | 4.Gang || 4,17 km/h |- | 5.Gang || 4,70 km/h |- | 6.Gang || 5,36 km/h |- | 7.Gang || 6,09 km/h |- | 8.Gang || 6,94 km/h |- | 9.Gang || 7,85 km/h |- | 10.Gang || 8,95 km/h |- | 11.Gang || 10,16 km/h |- | 12.Gang || 11,58 km/h |- | 13.Gang || 13,09 km/h |- | 14.Gang || 14,91 km/h |- | 15.Gang || 16,94 km/h |- | 16.Gang || 21,82 km/h |- | 17.Gang || 28,32 km/h |- | 18.Gang || 36,37 km/h |- ! RÜCKWÄRTSGÄNGE !! |- | 1.Gang || 2,19 km/h |- | 2.Gang || 3,65 km/h |- | 3.Gang || 4,17 km/h |- | 4.Gang || 6,09 km/h |- | 5.Gang || 6,94 km/h |- | 6.Gang || 10,16 km/h |- |} ==Zapfwelle== * Elektrohydraulisch-betätigte, unabhängige und unter Last schaltbare Motorzapfwelle * Stummel = 1 3/4- 20 teilig * Einfach schaltbar, 1.000 U/min. * 1.000 U/min. bei 2.091 U/min.- Motordrehzahl Übertragbare Leistung = 194,4 DIN-PS (Ab 2001 = 202,8 DIN-PS) * Oder 1.052 U/min. mit Nenndrehzahl Übertragbare Leistung = 185,7 DIN-PS (Ab 2001 = 186,9 DIN-PS) ==Bremsen== * Pedal-betätigte, hydraulisch-nasse Scheibenbremse, auf die Differentialseitenwellen wirkend, als Einzelradbremse ausgebildet * Handhebel-betätigte Feststellbremse, auf die Getriebeeingangswelle wirkend Optional mit hydraulischer Anhängerbremse ==Achsen== * Pendelnd-gelagerte Teleskop-Vorderachse "Optional:" * Pendelnd-gelagerte Planeten-Vorderachse mit zentraler Gelenkwelle Verstellbare Spurweite = 1.522 bis 2.233 mm * Starre Hinterachse mit Kegelradgetriebe und Planeten-Untersetzungsgetriebe Pedal-betätigte Differentialsperre * Verstellbare Spurweite = 1.562 bis 3.200 mm * Vordere Achslast-4 WD = 3.311 kg * Hintere Achslast-4 WD = 5.468 kg ==Lenkung== * Hydrostatische Lenkung Ein doppelt-wirkender Lenkzylinder ==Hydrauliksystem und Kraftheber== * Hydraulischer Regelkraftheber, mit elektronischer Hubwerksregelung (EHR) * Einfachwirkender Hubzylinder * Sicherheitsventil des Hauptzylinders auf 210 bar eingestellt * Dreipunktaufhängung der Kategorie II/III "Funktionen:" * Heben, Senken, Neutral- und Schwimmstellung, Zugwiderstands- und Lageregelung sowie stufenlose Mischregelung * Hubhöhenbegrenzung und hydraulisch-mechanische Transportsicherung * Geschlossenes System mit max. Förderleistung = 112,4 l/min. bei 197 bar und 85,9 l/min. bei 185 bar Leistung der Hydraulik = 26,6 kW * Max. Hubkraft an den Koppelpunkten = 8.940 kg Max. Hubkraft 610 mm hinter den Koppelpunkten = 10.116 kg "HIGH-FLOW-Variante:" * Geschlossenes System mit max. Förderleistung = 148,8 l/min. bei 196 bar und 135,9 l/min. bei 179 bar Leistung der Hydraulik = 38,9 kW ==Steuergeräte== * Drei einfach- oder doppelt-wirkendes Steuergeräte Optional bis zu vier doppelt-wirkende Steuergeräte ==Elektrische Ausrüstung== "12 Volt-Einrichtung:" ==Maße und Abmessungen== * Länge über alles = 5.156 mm * Breite bei kleinster Spurweite = 2.494 mm * Höhe über Kabine = 3.073 mm * Radstand = 2.858 mm (Später = 3.073 mm) * Bodenfreiheit = 445 mm * Betriebsgewicht = 8.215 kg (4 WD = 8.779 kg) ==Bereifung== "Standardbereifung" * Vorne = 11.00-16 AS Front (4 WD = 14.9 R 28 AS) * Hinten = 18.4 R 38 AS "Optional:" * Vorne = 10.00-16 AS Front (4 WD = 14.9 R 30 AS) * Hinten = 18.4 R 42 AS ==Füllmengen== * Tankinhalt = 454,0 l * Motoröl mit Filter = 28,4 l * Kühlsystem = 26,0 l * Getriebe und Hydraulik = 83,3 l * Power-Shift = 5,7 l ==Verbrauch== * Kraftstoffverbrauch-NAVISTAR = 46,0 l/h bei 185,7 kW und Nenndrehzahl * Kraftstoffverbrauch-CUMMINS = 43,5 l/h bei 186,9 kW und Nenndrehzahl ==Kabine== * Auf Silentblöcken gelagerte AGCO-Sicherheitskabine, getönte Scheiben, Luftfedersitz mit Armlehnen, Lade- und Öldruckanzeige, Kraftstoff- und Temperaturanzeige, Traktormeter incl. Betriebsstundenzähler, Digitaluhr, elektrische dreistufen Heizung, zwei Außenspiegel, zwei Arbeitsscheinwerfer vorne und hinten und Klimaanlage ==Sonderausrüstung== * Zusatz-Gewichte * Vordere Kotflügel * Anhängerbremse * Allradantrieb ==Literatur & Weblinks== * AGCO-Allis-brochure * tractordata.com * digitalcommons.unl.edu (Test-Nr. 1782-A/00) <references /> {{:Traktorenlexikon: Navigation |HERSTELLER-LINK=Traktorenlexikon: AGCO-Allis |HERSTELLER= AGCO-Allis}} 7clq80l1etrnvjis7phj2s3zixrj3nk 1088113 1088105 2026-06-13T18:10:07Z Baupit 56622 /* Verbrauch */ 1088113 wikitext text/x-wiki {{:Traktorenlexikon: Navigation |HERSTELLER-LINK=Traktorenlexikon: AGCO-Allis |HERSTELLER= AGCO-Allis}} {{:Traktorenlexikon: Modell-Infobox | HERSTELLER = AGCO-ALLIS | MODELLREIHE = 9700 er - SERIE | MODELL = 9765 | BILD = | BILDBESCHREIBUNG = | BAUWEISE = Blockbauweise | PRODUKTIONSBEGINN = 1998 | PRODUKTIONSENDE = 2001 | STÜCKZAHL = | EIGENGEWICHT = 7.802 (4 WD: 8.345) | LÄNGE = 5.156 | BREITE = 2.494 | HÖHE = 3.073 | RADSTAND = 2.858 | BODENFREIHEIT = 445 | SPURWEITE = | SPURWEITE VORNE = (4 WD: 1.522-2.233) | SPURWEITE HINTEN = 1.562-3.299 | WENDERADIUS MIT LENKBREMSE = | WENDERADIUS OHNE LENKBREMSE = | BEREIFUNG VORNE = 11.00-16 ASF (4 WD: 14.9 R 30 AS) | BEREIFUNG HINTEN = 18.4 R 42 AS | LEISTUNG KW = 136,1 | LEISTUNG PS = 185 | NENNDREHZAHL = 2.200 | ZYLINDER = 6 | HUBRAUM = 8.705/8.268 | DREHMOMENTANSTIEG = 27/26 | KRAFTSTOFF = Diesel | KÜHLSYSTEM = Wasserkühlung | ANTRIEBSTYP = Heck- und Allradantrieb | GETRIEBE = 18 V/6 R oder 36 V/12 R | HÖCHSTGESCHWINDIGKEIT = 30 oder 40 | KATEGORIESORTIERUNG = }} Die im Jahr 1998 vorgestellte 9700 er-Serie war die letzte von AGCO gefertigte Baureihe, in der der Markenname "ALLIS" Verwendung fand. Eins der großen Modelle war der AGCO-ALLIS 9765, der eine Nennleistung von 185 DIN-PS aufwies. Anfangs setzte AGCO ein aufgeladenes Sechszylinder-NAVISTAR-Aggregat ein, das später durch ein aufgeladenes und mit Ladeluftkühlung bestückten Sechszylinder-CUMMINS-Aggregat ersetzt wurde. Das serienmäßige Triebwerk verfügte über 18/6-Gänge, was auf Wunsch auf 36/12-Gänge gesteigert werden konnte. Der AGCO-ALLIS 9765 bot dem Fahrer modernen Komfort, darunter eine verbesserte Rundumsicht, eine ergonomische Armlehnensteuerung und eine elektronische Regelung für den Heckkraftheber. Er war sowohl als Hinterrad- als auch als Allradantrieb erhältlich. ==Motor== * INTERNATIONAL-NAVISTAR, Typ: DT-530 , stehender wassergekühlter Viertakt-Sechszylinder-Reihen-Saugmotor mit Direkteinspritzung, hängenden Ventile, Kraftstoffpumpe, Leichtmetall-Kolben, BOSCH-Einspritzpumpe, mechanischer Drehzahlregler, Druckumlaufschmierung mittels Zahnradpumpe, BOSCH-Mehrloch-Einspritzdüsen, zahnradgetriebene Nockenwelle, DONALDSON-Trockenluftfilter, Ölkühler, Turbolader, siebenfach-gelagerte Kurbelwelle, auswechselbare Zylinderlaufbuchsen und Lamellenkühler mit Lüfter. * Bohrung = 116,59 mm, Hub = 135,89 mm * Verdichtungsverhältnis = 16,5:1 * Max. Drehmoment = 869 Nm bei 1.500 U/min. * Drehmomentanstieg = 27 % bei 1.800 U/min. * Mittlere Kolbengeschwindigkeit = 9,97 m/s * Max. Drehmomentanstieg = 46,6 % * Geregelter Drehzahlbereich = 650 bis 2.404 U/min. * Ladedruck = 1,75 bar "Alternativ:" * CUMMINS, Typ: C-8.3 T, stehender wassergekühlter Viertakt-Sechszylinder-Reihen-Saugmotor mit Direkteinspritzung, hängenden Ventile, Leichtmetall-Kolben, FIAMM-Kraftstofffilter, BOSCH-Einspritzpumpe, mechanischer BOSCH-Drehzahlregler, Ölkühler, NELSON-Schalldämpfer, Druckumlaufschmierung mittels Zahnradpumpe, BOSCH-Mehrloch-Einspritzdüsen, zahnradgetriebene Nockenwelle, DONALDSON-Trockenluftfilter incl. Vorfilter, Wastegate-Turbolader incl. Ladeluftkühlung, siebenfach-gelagerte Kurbelwelle, auswechselbare Zylinderlaufbuchsen, Lamellenkühler und Lüfter. * Bohrung = 114 mm, Hub = 135 mm * Verdichtungsverhältnis = 17,3:1 * Max. Drehmoment = 858 Nm bei 1.300 U/min. * Mittlere Kolbengeschwindigkeit = 9,90 m/s * Drehmomentanstieg = 26 % bei 1.800 U/min. * Geregelter Drehzahlbereich = 800 bis 2.480 U/min. * Max. Drehmomentanstieg = 43,7 % * Max. Ladedruck = 1,59 bar ==Kupplung== * Ölgekühlte-nasse Mehrscheibenkupplung ==Getriebe== * Im Ölbad laufendes POWERSHIFT-Getriebe * Die Steuerung der Gänge erfolgt durch hydraulische Kupplungspakete * Gangwechsel erfolgt ohne Betätigung der Kupplung oder Unterbrechung des Kraftflusses * System passt Motordrehzahl und Getriebeübersetzung automatisch an, schaltet bei leichter Last hoch, bei schwerer Last herunter 18 Vorwärts- und 6 Rückwärtsgänge * Optional als Kriechganggetriebe 36 Vorwärts- und 12 Rückwärtsgänge ==Geschwindigkeiten vor- und rückwärts== "Geschwindigkeiten mit Bereifung 18.4 R 42 AS" {| class="wikitable" |- ! bei Motordrehzahl (U/min) !! 2.200 |- | 1.Gang || 2,19 km/h |- | 2.Gang || 2,83 km/h |- | 3.Gang || 3,65 km/h |- | 4.Gang || 4,17 km/h |- | 5.Gang || 4,70 km/h |- | 6.Gang || 5,36 km/h |- | 7.Gang || 6,09 km/h |- | 8.Gang || 6,94 km/h |- | 9.Gang || 7,85 km/h |- | 10.Gang || 8,95 km/h |- | 11.Gang || 10,16 km/h |- | 12.Gang || 11,58 km/h |- | 13.Gang || 13,09 km/h |- | 14.Gang || 14,91 km/h |- | 15.Gang || 16,94 km/h |- | 16.Gang || 21,82 km/h |- | 17.Gang || 28,32 km/h |- | 18.Gang || 36,37 km/h |- ! RÜCKWÄRTSGÄNGE !! |- | 1.Gang || 2,19 km/h |- | 2.Gang || 3,65 km/h |- | 3.Gang || 4,17 km/h |- | 4.Gang || 6,09 km/h |- | 5.Gang || 6,94 km/h |- | 6.Gang || 10,16 km/h |- |} ==Zapfwelle== * Elektrohydraulisch-betätigte, unabhängige und unter Last schaltbare Motorzapfwelle * Stummel = 1 3/4- 20 teilig * Einfach schaltbar, 1.000 U/min. * 1.000 U/min. bei 2.091 U/min.- Motordrehzahl Übertragbare Leistung = 194,4 DIN-PS (Ab 2001 = 202,8 DIN-PS) * Oder 1.052 U/min. mit Nenndrehzahl Übertragbare Leistung = 185,7 DIN-PS (Ab 2001 = 186,9 DIN-PS) ==Bremsen== * Pedal-betätigte, hydraulisch-nasse Scheibenbremse, auf die Differentialseitenwellen wirkend, als Einzelradbremse ausgebildet * Handhebel-betätigte Feststellbremse, auf die Getriebeeingangswelle wirkend Optional mit hydraulischer Anhängerbremse ==Achsen== * Pendelnd-gelagerte Teleskop-Vorderachse "Optional:" * Pendelnd-gelagerte Planeten-Vorderachse mit zentraler Gelenkwelle Verstellbare Spurweite = 1.522 bis 2.233 mm * Starre Hinterachse mit Kegelradgetriebe und Planeten-Untersetzungsgetriebe Pedal-betätigte Differentialsperre * Verstellbare Spurweite = 1.562 bis 3.200 mm * Vordere Achslast-4 WD = 3.311 kg * Hintere Achslast-4 WD = 5.468 kg ==Lenkung== * Hydrostatische Lenkung Ein doppelt-wirkender Lenkzylinder ==Hydrauliksystem und Kraftheber== * Hydraulischer Regelkraftheber, mit elektronischer Hubwerksregelung (EHR) * Einfachwirkender Hubzylinder * Sicherheitsventil des Hauptzylinders auf 210 bar eingestellt * Dreipunktaufhängung der Kategorie II/III "Funktionen:" * Heben, Senken, Neutral- und Schwimmstellung, Zugwiderstands- und Lageregelung sowie stufenlose Mischregelung * Hubhöhenbegrenzung und hydraulisch-mechanische Transportsicherung * Geschlossenes System mit max. Förderleistung = 112,4 l/min. bei 197 bar und 85,9 l/min. bei 185 bar Leistung der Hydraulik = 26,6 kW * Max. Hubkraft an den Koppelpunkten = 8.940 kg Max. Hubkraft 610 mm hinter den Koppelpunkten = 10.116 kg "HIGH-FLOW-Variante:" * Geschlossenes System mit max. Förderleistung = 148,8 l/min. bei 196 bar und 135,9 l/min. bei 179 bar Leistung der Hydraulik = 38,9 kW ==Steuergeräte== * Drei einfach- oder doppelt-wirkendes Steuergeräte Optional bis zu vier doppelt-wirkende Steuergeräte ==Elektrische Ausrüstung== "12 Volt-Einrichtung:" ==Maße und Abmessungen== * Länge über alles = 5.156 mm * Breite bei kleinster Spurweite = 2.494 mm * Höhe über Kabine = 3.073 mm * Radstand = 2.858 mm (Später = 3.073 mm) * Bodenfreiheit = 445 mm * Betriebsgewicht = 8.215 kg (4 WD = 8.779 kg) ==Bereifung== "Standardbereifung" * Vorne = 11.00-16 AS Front (4 WD = 14.9 R 28 AS) * Hinten = 18.4 R 38 AS "Optional:" * Vorne = 10.00-16 AS Front (4 WD = 14.9 R 30 AS) * Hinten = 18.4 R 42 AS ==Füllmengen== * Tankinhalt = 454,0 l * Motoröl mit Filter = 28,4 l * Kühlsystem = 26,0 l * Getriebe und Hydraulik = 83,3 l * Power-Shift = 5,7 l ==Verbrauch== * Kraftstoffverbrauch-NAVISTAR = 46,0 l/h bei 136,6 kW und Nenndrehzahl * Kraftstoffverbrauch-CUMMINS = 43,5 l/h bei 137,4 kW und Nenndrehzahl ==Kabine== * Auf Silentblöcken gelagerte AGCO-Sicherheitskabine, getönte Scheiben, Luftfedersitz mit Armlehnen, Lade- und Öldruckanzeige, Kraftstoff- und Temperaturanzeige, Traktormeter incl. Betriebsstundenzähler, Digitaluhr, elektrische dreistufen Heizung, zwei Außenspiegel, zwei Arbeitsscheinwerfer vorne und hinten und Klimaanlage ==Sonderausrüstung== * Zusatz-Gewichte * Vordere Kotflügel * Anhängerbremse * Allradantrieb ==Literatur & Weblinks== * AGCO-Allis-brochure * tractordata.com * digitalcommons.unl.edu (Test-Nr. 1782-A/00) <references /> {{:Traktorenlexikon: Navigation |HERSTELLER-LINK=Traktorenlexikon: AGCO-Allis |HERSTELLER= AGCO-Allis}} pypu5nkc8pgyfhdy3a0dsy4t8onxxxw Benutzer:Ясамойла 2 122892 1088106 2026-06-13T17:27:13Z Ясамойла 116214 пачатак 1088106 wikitext text/x-wiki Жыву ў Эўропе. omibgah2t62ygsbw0ranqvd4jjqhvpf 1088107 1088106 2026-06-13T17:30:31Z Ясамойла 116214 дапаўненьне 1088107 wikitext text/x-wiki {{#babel:de-0|be-4|ru-4|cu-4|uk-2|en-2|fr-1}} Жыву ў Эўропе. fj3at0qn6fjsmswm1q28g1hnhobx4t1 1088108 1088107 2026-06-13T17:32:01Z Ясамойла 116214 дапаўненьне 1088108 wikitext text/x-wiki {{babel|de-0|be-4|ru-4|cu-4|uk-2|en-2|fr-1}} Жыву ў Эўропе. 2rj1gpaj7vgv8ywruxxgvn614t1epp8 Wikibooks:Babel: Vorlage:User be-4 4 122893 1088109 2026-06-13T17:37:19Z Ясамойла 116214 пачатак 1088109 wikitext text/x-wiki {{:Wikibooks:Babel: Vorlage:Babel field 4 |letter code size=1.5em |letter code=[[w:Belarussische Sprache|be]] |text size=0.83em |text=Гэты ўдзельнік валодае '''[[:Category:User be|беларускай]]''' мовай '''[[:Category:User be-4|амаль]]''' як роднай. |lang=be }}<includeonly>[[Category:User be-4|{{PAGENAME}}]]</includeonly> n2zhovhyg5ve305dph1l7f0vemyfupe Kategorie:User be-4 14 122894 1088110 2026-06-13T17:41:56Z Ясамойла 116214 пачатак 1088110 wikitext text/x-wiki {{KategorieTOC}} {| |{{:Wikibooks:Babel: Vorlage:Babel field 4 |letter code size=1.5em |letter code=[[w:Belarussische Sprache|be]] |text size=0.83em |text=Гэты ўдзельнік валодае '''[[:Category:User be|беларускай]]''' мовай '''[[:Category:User be-4|амаль]]''' як роднай. |lang=be }} |{{:Wikibooks:Babel: Vorlage:Text Unterkategorie|Belarussische|be}} |} [[Kategorie:User_be]] 73j200pxli116btx3g1isxktp87fwt7 1088130 1088110 2026-06-14T08:17:45Z Juetho 15246 Bezeichnung der Sprache 1088130 wikitext text/x-wiki {{KategorieTOC}} {| |{{:Wikibooks:Babel: Vorlage:Babel field 4 |letter code size=1.5em |letter code=[[w:Belarussische Sprache|be]] |text size=0.83em |text=Гэты ўдзельнік валодае '''[[:Category:User be|беларускай]]''' мовай '''[[:Category:User be-4|амаль]]''' як роднай. |lang=be }} |{{:Wikibooks:Babel: Vorlage:Text Unterkategorie|Belarussisch|be}} |} [[Kategorie:User_be]] 6thzoyng2yx0zj9j3vbf56igmbsd6vp Traktorenlexikon: AGCO-Allis 9775 0 122895 1088112 2026-06-13T18:08:20Z Baupit 56622 Neue Seite (vgl. [[WB:AZ]]) 1088112 wikitext text/x-wiki {{:Traktorenlexikon: Navigation |HERSTELLER-LINK=Traktorenlexikon: AGCO-Allis |HERSTELLER= AGCO-Allis}} {{:Traktorenlexikon: Modell-Infobox | HERSTELLER = AGCO-ALLIS | MODELLREIHE = 9700 er - SERIE | MODELL = 9775 | BILD = | BILDBESCHREIBUNG = | BAUWEISE = Blockbauweise | PRODUKTIONSBEGINN = 1998 | PRODUKTIONSENDE = 2001 | STÜCKZAHL = | EIGENGEWICHT = 8.521 | LÄNGE = 5.156 | BREITE = 2.494 | HÖHE = 3.073 | RADSTAND = 2.858 | BODENFREIHEIT = 445 | SPURWEITE = | SPURWEITE VORNE = 1.524-2.235 | SPURWEITE HINTEN = 1.562-3.200 | WENDERADIUS MIT LENKBREMSE = | WENDERADIUS OHNE LENKBREMSE = | BEREIFUNG VORNE = 14.9 R 34 AS | BEREIFUNG HINTEN = 18.4 R 46 AS | LEISTUNG KW = 152,2 | LEISTUNG PS = 207 | NENNDREHZAHL = 2.200 | ZYLINDER = 6 | HUBRAUM = 8.705/8.268 | DREHMOMENTANSTIEG = 24/26 | KRAFTSTOFF = Diesel | KÜHLSYSTEM = Wasserkühlung | ANTRIEBSTYP = Allradantrieb | GETRIEBE = 18 V/6 R oder 36 V/12 R | HÖCHSTGESCHWINDIGKEIT = 30 oder 40 | KATEGORIESORTIERUNG = }} Die 9700 er-Serie war die letzte von AGCO gefertigte Baureihe, in der der Markenname "ALLIS" Verwendung fand. Eins der großen Modelle war der AGCO-ALLIS 9775, der eine Nennleistung von 207 DIN-PS aufwies. Anfangs setzte AGCO ein Sechszylinder-NAVISTAR-Aggregat ein, das später durch ein Sechszylinder-CUMMINS-Aggregat, beide mit Turbolader und Ladeluftkühlung ersetzt wurde. Das serienmäßige Triebwerk verfügte über 18/6-Gänge, was auf Wunsch auf 36/12-Gänge gesteigert werden konnte. Der AGCO-ALLIS 9775 bot dem Fahrer modernen Komfort, darunter eine verbesserte Rundumsicht, eine ergonomische Armlehnensteuerung und eine elektronische Regelung für den Heckkraftheber. ==Motor== * INTERNATIONAL-NAVISTAR, Typ: DTA-530 , stehender wassergekühlter Viertakt-Sechszylinder-Reihen-Saugmotor mit Direkteinspritzung, hängenden Ventile, Kraftstoffpumpe, Leichtmetall-Kolben, BOSCH-Einspritzpumpe, mechanischer Drehzahlregler, Druckumlaufschmierung mittels Zahnradpumpe, BOSCH-Mehrloch-Einspritzdüsen, zahnradgetriebene Nockenwelle, DONALDSON-Trockenluftfilter, Ölkühler, Turbolader incl. Ladeluftkühlung, siebenfach-gelagerte Kurbelwelle, auswechselbare Zylinderlaufbuchsen und Lamellenkühler mit Lüfter. * Bohrung = 116,59 mm, Hub = 135,89 mm * Verdichtungsverhältnis = 16,5:1 * Max. Drehmoment = 939 Nm bei 1.300 U/min. * Drehmomentanstieg = 24 % bei 1.800 U/min. * Mittlere Kolbengeschwindigkeit = 9,97 m/s * Max. Drehmomentanstieg = 41,6 % * Geregelter Drehzahlbereich = 650 bis 2.404 U/min. * Ladedruck = 1,55 bar "Alternativ:" * CUMMINS, Typ: C-8.3 TA, stehender wassergekühlter Viertakt-Sechszylinder-Reihen-Saugmotor mit Direkteinspritzung, hängenden Ventile, Leichtmetall-Kolben, FIAMM-Kraftstofffilter, BOSCH-Einspritzpumpe, mechanischer BOSCH-Drehzahlregler, Ölkühler, NELSON-Schalldämpfer, Druckumlaufschmierung mittels Zahnradpumpe, BOSCH-Mehrloch-Einspritzdüsen, zahnradgetriebene Nockenwelle, DONALDSON-Trockenluftfilter incl. Vorfilter, Wastegate-Turbolader incl. Ladeluftkühlung, siebenfach-gelagerte Kurbelwelle, auswechselbare Zylinderlaufbuchsen, Lamellenkühler und Lüfter. * Bohrung = 114 mm, Hub = 135 mm * Verdichtungsverhältnis = 17,3:1 * Max. Drehmoment = 970 Nm bei 1.300 U/min. * Mittlere Kolbengeschwindigkeit = 9,90 m/s * Drehmomentanstieg = 26 % bei 1.799 U/min. * Geregelter Drehzahlbereich = 800 bis 2.460 U/min. * Max. Drehmomentanstieg = 46,6 % * Max. Ladedruck = 1,65 bar ==Kupplung== * Ölgekühlte-nasse Mehrscheibenkupplung ==Getriebe== * Im Ölbad laufendes POWERSHIFT-Getriebe * Die Steuerung der Gänge erfolgt durch hydraulische Kupplungspakete * Gangwechsel erfolgt ohne Betätigung der Kupplung oder Unterbrechung des Kraftflusses * System passt Motordrehzahl und Getriebeübersetzung automatisch an, schaltet bei leichter Last hoch, bei schwerer Last herunter 18 Vorwärts- und 6 Rückwärtsgänge * Optional als Kriechganggetriebe 36 Vorwärts- und 12 Rückwärtsgänge ==Geschwindigkeiten vor- und rückwärts== "Geschwindigkeiten mit Bereifung 18.4 R 46 AS" {| class="wikitable" |- ! bei Motordrehzahl (U/min) !! 2.200 |- | 1.Gang || 2,32 km/h |- | 2.Gang || 3,00 km/h |- | 3.Gang || 3,87 km/h |- | 4.Gang || 4,42 km/h |- | 5.Gang || 4,98 km/h |- | 6.Gang || 5,68 km/h |- | 7.Gang || 6,45 km/h |- | 8.Gang || 7,35 km/h |- | 9.Gang || 8,32 km/h |- | 10.Gang || 9,48 km/h |- | 11.Gang || 10,77 km/h |- | 12.Gang || 12,27 km/h |- | 13.Gang || 13,87 km/h |- | 14.Gang || 15,80 km/h |- | 15.Gang || 17,95 km/h |- | 16.Gang || 23,12 km/h |- | 17.Gang || 29,91 km/h |- | 18.Gang || 38,53 km/h |- ! RÜCKWÄRTSGÄNGE !! |- | 1.Gang || 2,32 km/h |- | 2.Gang || 3,87 km/h |- | 3.Gang || 4,42 km/h |- | 4.Gang || 6,45 km/h |- | 5.Gang || 7,35 km/h |- | 6.Gang || 10,77 km/h |- |} ==Zapfwelle== * Elektrohydraulisch-betätigte, unabhängige und unter Last schaltbare Motorzapfwelle * Stummel = 1 3/4- 20 teilig * Einfach schaltbar, 1.000 U/min. * 1.000 U/min. bei 2.091 U/min.- Motordrehzahl Übertragbare Leistung = 218,8 DIN-PS (Ab 2001 = 224,5 DIN-PS) * Oder 1.052 U/min. mit Nenndrehzahl Übertragbare Leistung = 207,5 DIN-PS (Ab 2001 = 207,2 DIN-PS) ==Bremsen== * Pedal-betätigte, hydraulisch-nasse Scheibenbremse, auf die Differentialseitenwellen wirkend, als Einzelradbremse ausgebildet * Handhebel-betätigte Feststellbremse, auf die Getriebeeingangswelle wirkend Optional mit hydraulischer Anhängerbremse ==Achsen== * Pendelnd-gelagerte Planeten-Vorderachse mit zentraler Gelenkwelle Verstellbare Spurweite = 1.524 bis 2.235 mm * Starre Hinterachse mit Kegelradgetriebe und Planeten-Untersetzungsgetriebe Pedal-betätigte Differentialsperre * Verstellbare Spurweite = 1.562 bis 3.200 mm * Vordere Achslast = 3.561 kg * Hintere Achslast = 5.520 kg ==Lenkung== * Hydrostatische Lenkung Ein doppelt-wirkender Lenkzylinder ==Hydrauliksystem und Kraftheber== * Hydraulischer Regelkraftheber, mit elektronischer Hubwerksregelung (EHR) * Einfachwirkender Hubzylinder * Sicherheitsventil des Hauptzylinders auf 210 bar eingestellt * Dreipunktaufhängung der Kategorie II/III "Funktionen:" * Heben, Senken, Neutral- und Schwimmstellung, Zugwiderstands- und Lageregelung sowie stufenlose Mischregelung * Hubhöhenbegrenzung und hydraulisch-mechanische Transportsicherung * Geschlossenes System mit max. Förderleistung = 113,2 l/min. bei 196 bar und 98,4 l/min. bei 182 bar Leistung der Hydraulik = 29,9 kW * Max. Hubkraft an den Koppelpunkten = 8.940 kg Max. Hubkraft 610 mm hinter den Koppelpunkten = 10.116 kg "HIGH-FLOW-Variante:" * Geschlossenes System mit max. Förderleistung = 148,8 l/min. bei 196 bar und 135,9 l/min. bei 179 bar Leistung der Hydraulik = 38,9 kW ==Steuergeräte== * Drei einfach- oder doppelt-wirkendes Steuergeräte Optional bis zu vier doppelt-wirkende Steuergeräte ==Elektrische Ausrüstung== "12 Volt-Einrichtung:" ==Maße und Abmessungen== * Länge über alles = 5.156 mm * Breite bei kleinster Spurweite = 2.494 mm * Höhe über Kabine = 3.073 mm * Radstand = 2.858 mm (Später = 3.073 mm) * Bodenfreiheit = 445 mm * Betriebsgewicht = 9.081 kg ==Bereifung== "Standardbereifung" * Vorne = 16.9 R 28 AS * Hinten = 18.4 R 42 AS "Optional:" * Vorne = 16.9 R 30 und 14.9 R 34 AS * Hinten = 20.8 R 42 und 18.4 R 46 AS ==Füllmengen== * Tankinhalt = 454,0 l * Motoröl mit Filter = 28,4 l * Kühlsystem = 26,0 l * Getriebe und Hydraulik = 83,3 l * Power-Shift = 5,7 l ==Verbrauch== * Kraftstoffverbrauch-NAVISTAR = 47,0 l/h bei 164,6 kW und Nenndrehzahl * Kraftstoffverbrauch-CUMMINS = 51,0 l/h bei 168,0 kW und Nenndrehzahl ==Kabine== * Auf Silentblöcken gelagerte AGCO-Sicherheitskabine, getönte Scheiben, Luftfedersitz mit Armlehnen, Lade- und Öldruckanzeige, Kraftstoff- und Temperaturanzeige, Traktormeter incl. Betriebsstundenzähler, Digitaluhr, elektrische dreistufen Heizung, zwei Außenspiegel, zwei Arbeitsscheinwerfer vorne und hinten und Klimaanlage ==Sonderausrüstung== * Zusatz-Gewichte * Vordere Kotflügel * Anhängerbremse * Allradantrieb ==Literatur & Weblinks== * AGCO-Allis-brochure * tractordata.com * digitalcommons.unl.edu (Test-Nr. 1794-A/00 und 1794/01) <references /> {{:Traktorenlexikon: Navigation |HERSTELLER-LINK=Traktorenlexikon: AGCO-Allis |HERSTELLER= AGCO-Allis}} 1cy1udm7iqvlffi8thm4bwa6wqnzphc Traktorenlexikon: AGCO-Allis 9785 0 122896 1088118 2026-06-13T18:36:33Z Baupit 56622 Neue Seite (vgl. [[WB:AZ]]) 1088118 wikitext text/x-wiki {{:Traktorenlexikon: Navigation |HERSTELLER-LINK=Traktorenlexikon: AGCO-Allis |HERSTELLER= AGCO-Allis}} {{:Traktorenlexikon: Modell-Infobox | HERSTELLER = AGCO-ALLIS | MODELLREIHE = 9700 er - SERIE | MODELL = 9785 | BILD = | BILDBESCHREIBUNG = | BAUWEISE = Blockbauweise | PRODUKTIONSBEGINN = 1998 | PRODUKTIONSENDE = 2001 | STÜCKZAHL = | EIGENGEWICHT = 8.528 | LÄNGE = 5.156 | BREITE = 2.494 | HÖHE = 3.073 | RADSTAND = 2.858 | BODENFREIHEIT = 445 | SPURWEITE = | SPURWEITE VORNE = 1.524-2.235 | SPURWEITE HINTEN = 1.562-3.200 | WENDERADIUS MIT LENKBREMSE = | WENDERADIUS OHNE LENKBREMSE = | BEREIFUNG VORNE = 16.9 R 30 AS | BEREIFUNG HINTEN = 20.8 R 42 AS | LEISTUNG KW = 169,9 | LEISTUNG PS = 231 | NENNDREHZAHL = 2.200 | ZYLINDER = 6 | HUBRAUM = 8.705/8.268 | DREHMOMENTANSTIEG = 16 | KRAFTSTOFF = Diesel | KÜHLSYSTEM = Wasserkühlung | ANTRIEBSTYP = Allradantrieb | GETRIEBE = 18 V/6 R oder 36 V/12 R | HÖCHSTGESCHWINDIGKEIT = 30 oder 40 | KATEGORIESORTIERUNG = }} Die 9700 er-Serie war die letzte von AGCO gefertigte Baureihe, in der der Markenname "ALLIS" Verwendung fand. Das Spitzenmodell war der AGCO-ALLIS 9785, der eine Nennleistung von 231 DIN-PS aufwies. Die ersten Jahre wurde ein Sechszylinder-NAVISTAR-Aggregat verwendet, das später durch ein Sechszylinder-CUMMINS-Aggregat, beide mit Turbolader und Ladeluftkühlung ersetzt wurde. Das serienmäßige Triebwerk verfügte über 18/6-Gänge, was auf Wunsch auf 36/12-Gänge gesteigert werden konnte. ==Motor== * INTERNATIONAL-NAVISTAR, Typ: DTA-530 , stehender wassergekühlter Viertakt-Sechszylinder-Reihen-Saugmotor mit Direkteinspritzung, hängenden Ventile, Kraftstoffpumpe, Leichtmetall-Kolben, BOSCH-Einspritzpumpe, mechanischer Drehzahlregler, Druckumlaufschmierung mittels Zahnradpumpe, BOSCH-Mehrloch-Einspritzdüsen, zahnradgetriebene Nockenwelle, DONALDSON-Trockenluftfilter, Ölkühler, Turbolader incl. Ladeluftkühlung, siebenfach-gelagerte Kurbelwelle, auswechselbare Zylinderlaufbuchsen und Lamellenkühler mit Lüfter. * Bohrung = 116,59 mm, Hub = 135,89 mm * Verdichtungsverhältnis = 16,5:1 * Geregelter Drehzahlbereich = 650 bis 2.404 U/min. "Alternativ:" * CUMMINS, Typ: C-8.3 TA, stehender wassergekühlter Viertakt-Sechszylinder-Reihen-Saugmotor mit Direkteinspritzung, hängenden Ventile, Leichtmetall-Kolben, FIAMM-Kraftstofffilter, BOSCH-Einspritzpumpe, mechanischer BOSCH-Drehzahlregler, Ölkühler, NELSON-Schalldämpfer, Druckumlaufschmierung mittels Zahnradpumpe, BOSCH-Mehrloch-Einspritzdüsen, zahnradgetriebene Nockenwelle, DONALDSON-Trockenluftfilter incl. Vorfilter, Wastegate-Turbolader incl. Ladeluftkühlung, siebenfach-gelagerte Kurbelwelle, auswechselbare Zylinderlaufbuchsen, Lamellenkühler und Lüfter. * Bohrung = 114 mm, Hub = 135 mm * Verdichtungsverhältnis = 17,3:1 * Max. Drehmoment = 918 Nm bei 1.498 U/min. * Mittlere Kolbengeschwindigkeit = 9,90 m/s * Drehmomentanstieg = 16 % bei 1.800 U/min. * Geregelter Drehzahlbereich = 800 bis 2.460 U/min. * Max. Drehmomentanstieg = 24,2 % * Max. Ladedruck = 1,83 bar ==Kupplung== * Ölgekühlte-nasse Mehrscheibenkupplung ==Getriebe== * Im Ölbad laufendes POWERSHIFT-Getriebe * Die Steuerung der Gänge erfolgt durch hydraulische Kupplungspakete * Gangwechsel erfolgt ohne Betätigung der Kupplung oder Unterbrechung des Kraftflusses * System passt Motordrehzahl und Getriebeübersetzung automatisch an, schaltet bei leichter Last hoch, bei schwerer Last herunter 18 Vorwärts- und 6 Rückwärtsgänge * Optional mit zusätzlichem Kriechganggetriebe 36 Vorwärts- und 12 Rückwärtsgänge ==Geschwindigkeiten vor- und rückwärts== "Geschwindigkeiten mit Bereifung 20.8 R 42 AS" {| class="wikitable" |- ! bei Motordrehzahl (U/min) !! 2.200 |- | 1.Gang || 2,24 km/h |- | 2.Gang || 2,89 km/h |- | 3.Gang || 3,74 km/h |- | 4.Gang || 4,26 km/h |- | 5.Gang || 4,82 km/h |- | 6.Gang || 5,49 km/h |- | 7.Gang || 6,23 km/h |- | 8.Gang || 7,10 km/h |- | 9.Gang || 8,03 km/h |- | 10.Gang || 9,15 km/h |- | 11.Gang || 10,39 km/h |- | 12.Gang || 11,84 km/h |- | 13.Gang || 13,39 km/h |- | 14.Gang || 15,25 km/h |- | 15.Gang || 17,32 km/h |- | 16.Gang || 22,31 km/h |- | 17.Gang || 29,87 km/h |- | 18.Gang || 37,18 km/h |- ! RÜCKWÄRTSGÄNGE !! |- | 1.Gang || 2,24 km/h |- | 2.Gang || 3,74 km/h |- | 3.Gang || 4,26 km/h |- | 4.Gang || 6,23 km/h |- | 5.Gang || 7,10 km/h |- | 6.Gang || 10,39 km/h |- |} ==Zapfwelle== * Elektrohydraulisch-betätigte, unabhängige und unter Last schaltbare Motorzapfwelle * Stummel = 1 3/4- 20 teilig * Einfach schaltbar, 1.000 U/min. * 1.006 U/min. bei 2.100 U/min.- Motordrehzahl Übertragbare Leistung = 236,6 DIN-PS * Oder 1.052 U/min. mit Nenndrehzahl Übertragbare Leistung = 231,1 DIN-PS ==Bremsen== * Pedal-betätigte, hydraulisch-nasse Scheibenbremse, auf die Differentialseitenwellen wirkend, als Einzelradbremse ausgebildet * Handhebel-betätigte Feststellbremse, auf die Getriebeeingangswelle wirkend Optional mit hydraulischer Anhängerbremse ==Achsen== * Pendelnd-gelagerte Planeten-Vorderachse mit zentraler Gelenkwelle Verstellbare Spurweite = 1.524 bis 2.235 mm * Starre Hinterachse mit Kegelradgetriebe und Planeten-Untersetzungsgetriebe Pedal-betätigte Differentialsperre * Verstellbare Spurweite = 1.562 bis 3.200 mm * Vordere Achslast = 3.366 kg * Hintere Achslast = 5.670 kg ==Lenkung== * Hydrostatische Lenkung Ein doppelt-wirkender Lenkzylinder ==Hydrauliksystem und Kraftheber== * Hydraulischer Regelkraftheber, mit elektronischer Hubwerksregelung (EHR) * Einfachwirkender Hubzylinder * Sicherheitsventil des Hauptzylinders auf 210 bar eingestellt * Dreipunktaufhängung der Kategorie II/III "Funktionen:" * Heben, Senken, Neutral- und Schwimmstellung, Zugwiderstands- und Lageregelung sowie stufenlose Mischregelung * Hubhöhenbegrenzung und hydraulisch-mechanische Transportsicherung * Geschlossenes System mit max. Förderleistung = 113,2 l/min. bei 197 bar und 98,8 l/min. bei 179 bar Leistung der Hydraulik = 29,4 kW * Max. Hubkraft an den Koppelpunkten = 8.940 kg Max. Hubkraft 610 mm hinter den Koppelpunkten = 10.116 kg "HIGH-FLOW-Variante:" * Geschlossenes System mit max. Förderleistung = 148,8 l/min. bei 196 bar und 135,9 l/min. bei 179 bar Leistung der Hydraulik = 38,9 kW ==Steuergeräte== * Drei einfach- oder doppelt-wirkendes Steuergeräte Optional bis zu vier doppelt-wirkende Steuergeräte ==Elektrische Ausrüstung== "12 Volt-Einrichtung:" ==Maße und Abmessungen== * Länge über alles = 5.156 mm * Breite bei kleinster Spurweite = 2.494 mm * Höhe über Kabine = 3.073 mm * Radstand = 2.858 mm (Später = 3.073 mm) * Bodenfreiheit = 445 mm * Betriebsgewicht = 9.036 kg ==Bereifung== "Standardbereifung" * Vorne = 16.9 R 30 AS * Hinten = 20.8 R 42 AS "Optional:" * Vorne = 14.9 R 34 AS * Hinten = 18.4 R 46 AS ==Füllmengen== * Tankinhalt = 454,0 l * Motoröl mit Filter = 28,4 l * Kühlsystem = 26,0 l * Getriebe und Hydraulik = 83,3 l * Power-Shift = 5,7 l ==Verbrauch== * Kraftstoffverbrauch-CUMMINS = 54,7 l/h bei 170,0 kW und Nenndrehzahl ==Kabine== * Auf Silentblöcken gelagerte AGCO-Sicherheitskabine, getönte Scheiben, Luftfedersitz mit Armlehnen, Lade- und Öldruckanzeige, Kraftstoff- und Temperaturanzeige, Traktormeter incl. Betriebsstundenzähler, Digitaluhr, elektrische dreistufen Heizung, zwei Außenspiegel, zwei Arbeitsscheinwerfer vorne und hinten und Klimaanlage ==Sonderausrüstung== * Zusatz-Gewichte * Vordere Kotflügel * Anhängerbremse * Allradantrieb ==Literatur & Weblinks== * AGCO-Allis-brochure * tractordata.com * digitalcommons.unl.edu (Test-Nr. 1795/01) <references /> {{:Traktorenlexikon: Navigation |HERSTELLER-LINK=Traktorenlexikon: AGCO-Allis |HERSTELLER= AGCO-Allis}} 3xxbmecxsbdujin7dvfzta7xp7qtsto Einfachbeuten 0 122897 1088128 2026-06-14T07:54:55Z Apiculturenaturelle 116387 Die Einfachbeute ist eine Bienenwohnung, die sich auf das Wesentliche konzentriert. Sie bietet den Bienen einen geschützten, gut isolierten Lebensraum und ermöglicht dem Imker eine naturnahe Betreuung mit möglichst wenigen Eingriffen. Ihr Ziel ist nicht die technische Optimierung der Honigproduktion, sondern die Schaffung stabiler Lebensbedingungen für das Bienenvolk. 1088128 wikitext text/x-wiki Einfachbeuten sind ökologische Bienenbehausungen, die nach dem Grundsatz der Einfachheit entwickelt wurden: möglichst wenige Bauteile, robuste Konstruktion und möglichst geringe Störungen des Bienenvolkes. Der Begriff wird nicht einheitlich verwendet, bezeichnet aber meist Beuten, die sich an den natürlichen Bedürfnissen der Bienen orientieren und dem Imker eine unkomplizierte Betriebsweise ermöglichen wie zum Beispiel die vom Französischen Imker Emile Warré entwickelte Volksbeute nun als Warré Beute weltweit bekannt die wohl auch den Prinzipien einer Einfachbeute entspricht. Typische Merkmale einer Einfachbeute sind: Ein großer, zusammenhängender Brutraum in dem sich das Volk natürlich entwickeln kann nach dem Vorbild einer natürlichen Bienenwohnung in Bäumen,wo Bienen bevorzugt seit Millionen von Jahren leben. Einfache und stabile Bauweise mit wenigen Einzelteilen. Gute Wärmedämmung durch dicke Holzwände wie zum Beispiel die 45 mm Dicke Bretterbeute des Biodynamischen Imkers Norbert Poeplau nach demPrinzip;einer Warré Beute. Wenige Eingriffe in das Brutnest, wodurch Stress für die Bienen reduziert wird. Langlebigkeit und Reparaturfreundlichkeit. Häufig eine Betriebsweise mit Schwerpunkt auf Bienengesundheit und Volksentwicklung statt auf maximalen Honigertrag. Die Idee dahinter ist, dass ein Bienenvolk umso stabiler und widerstandsfähiger bleibt, je weniger seine innere Organisation gestört wird. Jede Durchsicht des Brutraums verändert Temperatur, Luftfeuchtigkeit, Duftordnung und die Organisation der Arbeitsbienen. Eine Einfachbeute soll deshalb dem Volk ermöglichen, weitgehend ungestört zu leben und dennoch vom Imker betreut werden zu können. 47hycsvymq76tbf34xecpvizm3ap07u 1088129 1088128 2026-06-14T08:01:46Z Apiculturenaturelle 116387 1088129 wikitext text/x-wiki Einfachbeuten sind ökologische Bienenbehausungen, die nach dem Grundsatz der Einfachheit entwickelt wurden: möglichst wenige Bauteile, robuste Konstruktion und möglichst geringe Störungen des Bienenvolkes. Der Begriff wird nicht einheitlich verwendet, bezeichnet aber meist Beuten, die sich an den natürlichen Bedürfnissen der Bienen orientieren und dem Imker eine unkomplizierte Betriebsweise ermöglichen wie zum Beispiel die vom Französischen Imker Emile Warré entwickelte Volksbeute nun als Warré Beute weltweit bekannt die wohl auch den Prinzipien einer Einfachbeute entspricht. Typische Merkmale einer Einfachbeute sind: Ein großer, zusammenhängender Brutraum in dem sich das Volk natürlich entwickeln kann nach dem Vorbild einer natürlichen Bienenwohnung in Bäumen,wo Bienen bevorzugt seit Millionen von Jahren leben. Einfache und stabile Bauweise mit wenigen Einzelteilen. Gute Wärmedämmung durch dicke Holzwände wie zum Beispiel die [https://apiculturenaturelle.fr/de/poeplaubeute/ 45 mm Dicke Bretterbeute] des Biodynamischen Imkers Norbert Poeplau. Wenige Eingriffe in das Brutnest, wodurch Stress für die Bienen reduziert wird. Langlebigkeit und Reparaturfreundlichkeit. Häufig eine Betriebsweise mit Schwerpunkt auf Bienengesundheit und Volksentwicklung statt auf maximalen Honigertrag. Die Idee dahinter ist, dass ein Bienenvolk umso stabiler und widerstandsfähiger bleibt, je weniger seine innere Organisation gestört wird. Jede Durchsicht des Brutraums verändert Temperatur, Luftfeuchtigkeit, Duftordnung und die Organisation der Arbeitsbienen. Eine Einfachbeute soll deshalb dem Volk ermöglichen, weitgehend ungestört zu leben und dennoch vom Imker betreut werden zu können. dsu58f4sxcyi6w6r2uhrt73426jmqq0 1088131 1088129 2026-06-14T08:59:43Z Apiculturenaturelle 116387 1088131 wikitext text/x-wiki Einfachbeuten sind ökologische Bienenbehausungen, die nach dem Grundsatz der Einfachheit entwickelt wurden: möglichst wenige Bauteile, robuste Konstruktion und möglichst geringe Störungen des Bienenvolkes.Eine Einfachbeute hat idealer weise einen quadratischen Innenraum mit 8 vertikalen [https://apiculturenaturelle.fr/de/rahmen-einfachbeute/ Mellifera-Hochrahmen] und einem Volumen von etwa 45 Litern und wird vorrangig im Mobilbau kann aber auch im Stabilbau betrieben werden. Der Begriff wird nicht einheitlich verwendet, bezeichnet aber meist Beuten, die sich an den natürlichen Bedürfnissen der Bienen orientieren und dem Imker eine unkomplizierte Betriebsweise ermöglichen wie zum Beispiel die vom Französischen Imker Emile Warré entwickelte Volksbeute nun als Warré Beute weltweit bekannt die wohl auch den Prinzipien einer Einfachbeute entspricht. Typische Merkmale einer Einfachbeute sind: Ein großer, zusammenhängender Brutraum in dem sich das Volk natürlich entwickeln kann nach dem Vorbild einer natürlichen Bienenwohnung in Bäumen,wo Bienen bevorzugt seit Millionen von Jahren leben. Einfache und stabile Bauweise mit wenigen Einzelteilen. Gute Wärmedämmung durch dicke Holzwände wie zum Beispiel die [https://apiculturenaturelle.fr/de/poeplaubeute/ 45 mm Dicke Bretterbeute] des Biodynamischen Imkers Norbert Poeplau. Wenige Eingriffe in das Brutnest, wodurch Stress für die Bienen reduziert wird. Langlebigkeit und Reparaturfreundlichkeit. Häufig eine Betriebsweise mit Schwerpunkt auf Bienengesundheit und Volksentwicklung statt auf maximalen Honigertrag. Die Idee dahinter ist, dass ein Bienenvolk umso stabiler und widerstandsfähiger bleibt, je weniger seine innere Organisation gestört wird. Jede Durchsicht des Brutraums verändert Temperatur, Luftfeuchtigkeit, Duftordnung und die Organisation der Arbeitsbienen. Eine Einfachbeute soll deshalb dem Volk ermöglichen, weitgehend ungestört zu leben und dennoch vom Imker betreut werden zu können. 42ulaowyt3js239rngycky9si7fmxe0