Wikibooks dewikibooks https://de.wikibooks.org/wiki/Hauptseite MediaWiki 1.47.0-wmf.23 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: Fendt 0 6952 1092061 1090008 2026-10-10T15:45:01Z Bent07 117026 /* 1000 Vario */ Anstatt der 380-517 PS Motorleistung 396 als korrekte Zahl ergänzt 1092061 wikitext text/x-wiki {{:Traktorenlexikon: Navigation}} [[Datei:Fendt turbomatik frontlader.jpg|thumb|Fendt Farmer Turbomatik mit Frontlader]] [[Datei:FendtBadge-crop.jpg|alternativtext=|mini|Fendt Kühler-Logo]] '''Fendt''' ist ein deutscher Traktorenhersteller mit Sitz in [[w:Marktoberdorf|Marktoberdorf]] im Allgäu und seit Ende der 1990er Jahre eine Marke des US-amerikanischen Konzerns [[Traktorenlexikon: AGCO|AGCO]]. Produktionsstandorte befinden sich in Marktoberdorf (Allgäu), Asbach-Bäumenheim und Hohenmölsen. ==Geschichte== [[Datei:Fendt Marktoberdorf aerial 2005.jpg|thumb|Fendt-Werk in Marktoberdorf im Jahr 2006 (rechts im Bild)]] Als Johann Georg Fendt begann, in seiner Mechanikerwerkstatt Traktoren zu bauen, ahnte er noch wenig von den großen Veränderungen, die die Traktorentechnik in diesem Jahrhundert durchlaufen sollte. Die Richtung stand von Anfang an fest: Die Kunden durch neue technische Lösungen, Wirtschaftlichkeit und Zuverlässigkeit zu überzeugen. Am 9. Dezember 1933 starb Joh. Georg Fendt. Firmeninhaberin wurde seine Witwe Kreszentia Fendt, Betriebsleiter sein ältester Sohn Xaver, der 1934 auch die Meisterprüfung ablegte. Die Xaver Fendt & Co. wurde am 31. Dezember 1937 in das Handelsregister in Kempten mit den Gesellschaftern Kreszentia, Xaver und Hermann Fendt eingetragen. === Entwicklungsgeschichte in Jahreszahlen === *'''1927''' Es entstand zunächst ein Kleinschlepper mit einem Benzinmotor, aber bereits einem fahrunabhängigen Mähwerk. *'''1928''' Der erste europäische Dieselkleinschlepper mit 6 PS, Anbaupflug und fahrunabhängigem Mähwerk. *'''1930''' wurden die ersten Schlepper verkauft. *'''1933''' legte Joh. Georg Fendt die erste Serie auf und stellte auf der Landwirtschaftsausstellung in München aus. Erstmals konnten es sich auch klein- und mittelbäuerliche Betriebe wirtschaftlich leisten, das Pferd zu ersetzen. Sein Name war Programm: ''Fendt-Dieselroß'' *'''1937''' Das Dieselross F 18 (16 PS) konnte mit einer fahrunabhängigen und lastschaltbaren Zapfwelle ausgestattet werden. *'''1938''' wurde der 1000ste Dieselroß-Traktor, ein F 18 mit 16 PS, fertiggestellt. Die Form des Dieselross F 22 (22 PS) erregte mit dem stehenden Motor und Kühler-Ventilator Aufsehen bei den Landwirten. Das Dieselroß F 22 hatte schon eine Reihe von Merkmalen der Traktoren der 1950er-Jahre: Zweizylinder-Motor, stehender Kühler und 4-Gang-Getriebe. *[[Datei:1928 - Fendt - Grasmäher 4PS .tiff-crop.png|mini|Fendt Grasmäher, 4 PS (1928)]]'''1942''' führte Dieselöl-Knappheit und das Verbot des Einsatzes von Dieselschleppern zur Entwicklung des Holzgasgenerator-Schleppers mit 25 PS. Nach dem Zweiten Weltkrieg expandierte das Unternehmen unter der Leitung Xaver Fendts und seiner jüngeren Brüder Hermann und Paul Fendt ungewöhnlich rasch. [[Datei:Fendt Dieselross F17 WG, 18 PS, Bj. 1959 (2012-07-18).JPG|mini|hochkant=1.2|Fendt Dieselross F 17 WG von 1959, im Jahr 2012 noch bei der Pflege von öffentlichen Anlagen im Einsatz]] *'''1949''' Vom Dieselross F 18 (18 PS) wurden monatlich 20 Stück produziert. *'''1952''' wurde das 12 PS starke Dieselroß F 12 L vorgestellt, das ab 1953 in Großserie ging. Im Jahre *'''1953''' kam der 12 PS starke Fendt-Geräteträger mit vier Anbauräumen auf den Markt, dem 1959 für sein geniales Einmann-System die höchste DLG-Anerkennung verliehen wurde. *'''1957''' wurde mit der Serienfertigung des [[Traktorenlexikon: Fendt#GT (Geräteträger)|Geräteträgers]] begonnen. *'''1958''' hat man das aktuelle Programm durch die neue "ff"-Reihe mit den Typen [[#Fix|Fix]], [[#Farmer|Farmer]] und [[#Favorit|Favorit]] abgelöst. Dabei wurde das Design geändert, und die Motorleistung von 15 bis 80 PS angeboten. Der [[#Favorit 1|Favorit 1]] war richtungweisend in Form und technischer Ausstattung, wie zum Beispiel der 40 PS-Motor und das Vielgang-Feinstufengetriebe. *'''1961''' konnte bereits der 100.000ste Fendt, ein "Farmer 2" mit 35 PS, fertiggestellt werden. Besonders erfolgreich entwickelten sich die Geräteträger, ein Mechanisierungssystem von der Saat bis zur Ernte, von denen bisher rund 60.000 Stück abgesetzt werden konnten. *'''1962''' Der erste selbstfahrende Rübenvollernter auf der Basis des Fendt-GT-Einmann-Systems wurde mit der höchsten DLG-Auszeichnung prämiert. *'''1968''' Farmer 3 S (48 PS) mit stufenloser Anfahrautomatik (Turbomatik) – seine Technik wurde zum Vorbild. *'''1970''' übernahm Fendt Teile der Firma [[Traktorenlexikon: Dechentreiter|Lely-Dechentreiter]] *'''1971''' Mit dem Universaltransporter und Erntefahrzeug Agrobil S stellte Fendt 1971 eine landtechnische Neuheit vor. *'''1975''' Spezialtraktoren für Wein-, Hopfen- und Obstbau (42, 50 und 65 PS). *'''1976''' Ein neues Marktsegment eröffneten die vorgestellten Favorit-Großtraktoren mit bis zu 150 PS. *'''1980''' brachte Fendt die Farmer 300er-Reihe auf den Markt, die mit technischen Pionierleistungen wie 40 km/h und gummigelagerter Kabine maßgeblich zu den Markterfolgen der letzten Jahre beigetragen hat. * „Wer besser sieht, kann besser arbeiten“ – nach diesem Motto hat Fendt den 380 GTA als Original-Freisicht-Traktor ohne Motorhaube im Jahre '''1984''' realisiert. Sein Geheimnis ist das geniale Konzept mit Unterflurmotor. *'''1985''' wird Fendt erstmals Marktführer in Deutschland. *'''1987''' High-Tech in kompakter Dimension zeichnete die 200er Baureihe von 40 bis 75 PS aus. Als Standard- oder Spezialtraktor für den Wein- und Obstbau. *'''1993''' Die 800er sind die ersten Großtraktoren der Welt mit Turboshift, hydropneumatischer Kabinen- und Vorderachsfederung und 50 km/h. Die leistungsstärkste Maschine: Favorit 824 mit 230 PS. *'''1994''' Die Favorit 500 C-Baureihe (94 bis 140 PS) - integriert die Erfolgsmerkmale wie 50 km/h, Federung und Turboshift in der Mittelklasse; Kunden und Fachwelt sind begeistert. *'''1995''' Das wegweisende Systemfahrzeug Xylon (110 bis 140 PS) kommt auf den Markt. Ein universell einsetzbares Fahrzeug für Landwirtschaft, Landschaftspflege und Kommunen. *'''1996''' Meilenstein: Vario 926 mit 260 PS, der erste Großtraktor der Welt mit dem stufenlosen Fendt-Vario-Getriebe. Eine Revolution im Getriebebau. Als führende High-Tech- und Qualitätsmarke wird Fendt 1997 Teil des weltweit agierenden [[Traktorenlexikon: AGCO|AGCO-Konzerns]]. *'''1997''' Einführung der neuen Traktorengeneration Farmer 300 C von 75 bis 95 PS. Fendt präsentiert mit der Baureihe Favorit das Vario-Programm von 170 bis 260 PS mit stufenloser Antriebstechnik. Mit der Vorstellung der neuen Vario-Baureihe Favorit 700 mit 140 und 160 PS setzt Fendt 1998 erneut Maßstäbe für die Zukunft. Das innovative Bediensystem, die Fendt-Variotronic, wird anschließend international ausgezeichnet. *'''1998''' Der 380 GTA-Turbo mit 95 PS und der 370 GMT mit 75 PS - die Original-Freisichtschlepper *'''1999''' Mit Vario 2000 präsentiert Fendt ein komplettes Programm von 86–286 PS. Dieses Programm überträgt die Vario-Vorteile erstmals in die untere Mittelklasse bis 86 PS. ** Farmer 400 Vario von 86 bis 110 PS, die neue Dimension in der Mittelklasse ** Baureihe Favorit 700 Vario mit neuer Technik und ergänzt um die Modelle 711 (115 PS) und 712 (125 PS) ** Favorit 900 Vario, die neue Generation für schwerste Einsätze im Leistungssegment 180 bis 270 PS. Der neue Slogan: „Wer führt, fährt Vario!“ *'''2001''' Neu im Fendt- Programm ist der 300 C von 75 bis 100 PS, sowie der 412 Vario mit 120 PS *'''2002''' Farmer 200 V/P von 65 bis 94 PS ** 800 Vario TMS von 168 bis 195 PS ** 900 Vario TMS von 198 bis 310 PS ** Fendt-Mähdrescher ** Fendt-Rund- und Quaderballenpressen *'''2004''' sorgen zwei Innovationen für Aufmerksamkeit: die niveaugeregelte Vorderachsfederung für Schmalspurtraktoren und das GPS-gestützte Spurführungssystem Auto-Guide-System. *'''2005''' Vom 6-PS-Dieselross zum 900 Vario TMS mit bis zu 310 PS in 75 Jahren – dieses Jubiläum feierte Marktoberdorf gemeinsam mit Fendtlern aus aller Welt. ** 936 Vario TMS mit einer max. Leistung von 358 PS und als erster Standardtraktor mit 60-km/h-Getriebe. * 200x … ==Typen== Es wurden Schlepper mit folgenden Typenbezeichnungen vertrieben ('''die Liste ist noch unvollständig!'''): === Dieselross === [[File:Fendt Dieselross 6 PS 01.jpg|thumb|Fendt Dieselross F1 6 PS]] [[File:Fendt Dieselross F15G6 (1954).jpg|thumb|Fendt Dieselross F15 G6]] [[File:Fendt F40.jpg|thumb|Fendt Dieselross F40]] Das Fendt Dieselross ist die legendäre Ursprungsbaureihe, mit der das Unternehmen Fendt ab den 1930er Jahren den Grundstein für seinen Aufstieg zum führenden Traktorenhersteller legte. Der Name wurde 1930 mit dem ersten europäischen Kleinschlepper mit 6 PS Dieselmotor, dem F1, geprägt und stand über Jahrzehnte als Synonym für Zuverlässigkeit und technischen Fortschritt in der Landwirtschaft. Die Serie entwickelte sich von einfachen Einzylinder-Maschinen hin zu leistungsstarken Mehrzylindern und deckte bis zum Ende der Produktion im Jahr 1958 ein breites Spektrum für klein- und mittelbäuerliche Betriebe ab. Technisch waren die Dieselross-Modelle Vorreiter in vielen Bereichen. Während die frühen Typen wie das F12, F15 oder F18 noch mit Verdampferkühlung oder einfacher Luftkühlung arbeiteten, führte Fendt später moderne wassergekühlte Aggregate und innovative Getriebekonstruktionen ein. Ein Meilenstein war die Einführung der gefederten Vorderachse und der motorabhängigen Zapfwelle, die das Dieselross zu einem universell einsetzbaren Arbeitsgerät machten. Die Motoren stammten überwiegend von MWM oder wurden in Kooperation entwickelt. Charakteristisch für diese Ära war das markante Design mit der hohen, schmalen Kühlermaske und dem tiefen, oft unverkennbaren Klang der Ein- und Zweizylinder-Dieselmotoren. Die Baureihe unterteilte sich in zahlreiche Varianten, von den leichten Modellen für den Gartenbau bis hin zu den schweren Ackerschleppern wie dem F40. Mit dem Übergang zur Farmer- und Favorit-Serie Ende der 1950er Jahre wurde der Markenname Dieselross offiziell abgelöst, blieb jedoch als Ehrentitel auf den Motorhauben der frühen Farmer-Modelle erhalten. Heute gelten die Dieselross-Schlepper als die begehrtesten Sammlerobjekte der deutschen Traktorengeschichte und werden für ihre robuste Mechanik und ihre Bedeutung für die Motorisierung der Landwirtschaft geschätzt. {{Wikipedia1|Fendt Dieselross}} {| class="wikitable" | '''Dieselross''' (1928–1961) |- | valign="top" | * {{:Traktorenlexikon: Create|Grasmäher 1928|Grasmäher 1928}} * {{:Traktorenlexikon: Create|Dieselross 6 PS 1930|Dieselross 6 PS 1930}} * {{:Traktorenlexikon: Create|Dieselross F9 1930-1936|Dieselross F9 1930-1936}} * {{:Traktorenlexikon: Create|Dieselross F12 1936-1937|Dieselross F12 1936-1937}} * [[Traktorenlexikon: Fendt Dieselross F12|Dieselross F12]] 8,8 kW (12 PS) * [[Traktorenlexikon: Fendt Dieselross F15|Dieselross F15]] 11 kW (15 PS) * [[Traktorenlexikon: Fendt Dieselross F15 G6|Dieselross F15 G6]] 11 kW (15 PS) * [[Traktorenlexikon: Fendt Dieselross F17|Dieselross F17]] 12,5 kW (17 PS) * [[Traktorenlexikon: Fendt Dieselross F18|Dieselross F18]] 12/13 kW (16/18 PS) * [[Traktorenlexikon: Fendt Dieselross F18H|Dieselross F18H]] 13 kW (18 PS) * [[Traktorenlexikon: Fendt Dieselross F18 G|Dieselross F18 G]] 13/14,5 kW (18/20 PS) * [[Traktorenlexikon: Fendt Dieselross F20|Dieselross F20]] 14,5 kW (20 PS) * {{:Traktorenlexikon: Create|Dieselross F22}} 16 kW (22 PS) * [[Traktorenlexikon: Fendt Dieselross F24|Dieselross F24]] 17 kW (24 PS) * {{:Traktorenlexikon: Create|Dieselross F25}} 21 kW (25 PS) * {{:Traktorenlexikon: Create|Dieselross F25A}} 18 kW (25 PS) * [[Traktorenlexikon: Fendt Dieselross F28|Dieselross F28]] 21 kW (28 PS) * [[Traktorenlexikon: Fendt Dieselross F40|Dieselross F40]] 29 kW (40 PS) * {{:Traktorenlexikon: Create|Dieselross G25 Holzgas}} 18 kW (25 PS) * [[Traktorenlexikon: Fendt Dieselross F236|Dieselross F236]] 14,5 kW (20 PS) * [[Traktorenlexikon: Fendt Dieselross F237|Dieselross F237]] 17 kW (24 PS) * [[Traktorenlexikon: Fendt Dieselross FL114|Dieselross FL114]] 8 kW (12 PS) * [[Traktorenlexikon: Fendt Dieselross FLS237|Dieselross FLS237]] 18 kW (24 PS) |} === GT (Geräteträger) === 1953 wurde erstmals der Geräteträger F 12 GT der Öffentlichkeit auf der DLG-Ausstellung vorgestellt. Die für den Einmannbetrieb konstruierte ''selbstfahrende Arbeitsmaschine'' hatte vier Anbauräume: vor, unter, auf und hinter dem Schlepper. Durch das ''Fendt-Einmannsystem'' waren alle Anbauten ohne einen zweiten Mann möglich, und durch Vorführungen und Ausbildungskurse konnten Bauern für dieses System so begeistert werden, dass Fendt als einziger Gerätehersteller der Durchbruch in wirtschaftliche interessante Stückzahlen gelang. Hermann Fendt verwendete eine Konstruktion, die im Gegensatz zu allen anderen Herstellern nur einen Längsträger zum Anbau von Geräten hatte. Dieser Längsträger war starr mit der Vorderachse sowie drehbar mit der Schlepperhinterachse verbunden. Dadurch war es möglich, dass die angebauten Werkzeuge den Pendelbewegungen der Vorderachse folgten. 1957 wurde dieser erste Geräteträger dann zur Produktion freigegeben. Da die Motorisierung nicht ausreichte, wurde 1958 der F 220 GT vorgestellt, der einer Motorleistung von 19 PS hatte. [[Datei:Fendt F225GT (1961, 25 PS).jpg|thumb|[[Traktorenlexikon: Fendt F 225 GT|F 225 GT]]]] Da Fendt immer noch die schlechte Sicht auf die Anbaugeräte störte, wurde mit der Entwicklung eines Schleppers mit Unterflurmotor begonnen. Mit dem F 250 GT stellte man dann den ersten Schlepper mit Unterflurmotor vor. Er hatte den Motor unter dem Fahrerstand, und somit freie Sicht auf die Anbaugeräte an der Zwischenachse, da keine störende Motorhaube mehr im Sichtfeld war. In den 1970er Jahren folgten der F 255 GT und der F 275 GT, die dann eine feste Kabine besaßen. Diese gab es wahlweise mit langem Holm (GT) oder kurzem Holm (GTF). Im Jahr 1984 wurden sie dann durch die 300er Baureihe abgelöst. Der erste war der F 380 GTA (Geräteträger Allrad), und es folgte der F 360 GT/GTA sowie weitere Modelle von 50 bis 115 PS. Folgende Abkürzungen wurden verwendet: * '''GT:''' Geräteträger mit langen Holm * '''GTF:''' Geräteträger kurzer Holm (auch Kompakt GT oder Futterernte-Geräteträger genannt) * '''GTH:''' Geräteträger mit langen Holm und Hochradversion, dadurch mehr Bodenfreiheit * '''GTA:''' Allradgeräteträger * '''GHA:''' Allradgeräteträger in Hochradversion * '''GKA:''' Allradgeräteträger in Kommunalausführung (teilweise mit Großraumkabine des 390 GTA) [[Datei:Fendt F 345 GT 100 1744.jpg|thumb|[[Traktorenlexikon: Fendt F 345 GT|F 345 GT]]]] [[File:Fendt 370GT und Hürlimann D115.jpg|thumb|Fendt 370GT und Hürlimann D115]] Die Geräteträger werden heute nicht mehr produziert, da die Firma AGCO in Marktoberdorf im Jahr 2004 beschlossen hat, die Produktion der Geräteträger nach 40 Jahren aus wirtschaftlichen Gründen einzustellen. {{Wikipedia1|Fendt GT}} {| class="wikitable" | '''GT''' (1957–1958) || '''200 GT''' (1958–1993) || '''300 GT''' (1984–2004) |- | valign="top" | * [[Traktorenlexikon: Fendt F 12 GT|F 12 GT]] 9 kW (12 PS) | valign="top" | * [[Traktorenlexikon: Fendt F220 GT|F 220 GT]] 14 kW (19 PS) * [[Traktorenlexikon: Fendt F 225 GT|F 225 GT]] 18 kW (25 PS) * [[Traktorenlexikon: Fendt F 230 GT|F 230 GT]] 22 kW (30 PS) * [[Traktorenlexikon: Fendt F231 GT|F 231 GT]] 24/26 kW (32/35 PS) * [[Traktorenlexikon: Fendt F250 GT|F 250 GT]] 33 kW (45 PS) * {{:Traktorenlexikon: Create|F 255 GT}} 37/40 kW (50/55 PS) * [[Traktorenlexikon: Fendt F275 GT|F 275 GT]] 51 kW (70 PS) | valign="top" | * [[Traktorenlexikon: Fendt F 345 GT|F 345 GT]] 33 kW (45 PS) * {{:Traktorenlexikon: Create|F 350 GT|F 350 GT}} * {{:Traktorenlexikon: Create|F 360 GTA}} 44 kW (60 PS) * [[Traktorenlexikon: Fendt F365 GTA|F 365 GTA]] 48 kW (65 PS) * {{:Traktorenlexikon: Create|F 370 GT|F 370 GT}} * {{:Traktorenlexikon: Create|F 380 GTA}} 59 kW (80 PS) * {{:Traktorenlexikon: Create|F 390 GT|F 390 GT}} * [[Traktorenlexikon: Fendt F395 GTA Freisicht|F 395 GTA Freisicht]] * {{:Traktorenlexikon: Create|F 380 GTA Turbo|F 380 GTA Turbo}} |} === Fix === [[File:Fendt Fix 2 im "Ländle".jpg|thumb|Fendt Fix 2]] Die Fendt Fix Baureihe war eine Serie von Kleinschleppern, die ab Ende der 1950er Jahre als Einstiegssegment unterhalb der Farmer-Baureihe produziert wurde. Die Serie richtete sich an kleinere landwirtschaftliche Betriebe sowie an Nebenerwerbslandwirte und zeichnete sich durch eine einfache, robuste Technik aus. Zu den bekanntesten Modellen zählen der Fix 1 und der Fix 2, die in verschiedenen Varianten wie der luftgekühlten (FL) oder wassergekühlten (FW) Ausführung sowie als weiterentwickelte Versionen wie der Fix 16, Fix 2D oder Fix 2E erschienen. Technisch setzten die Fix-Traktoren überwiegend auf Motoren des Herstellers MWM (Motorenwerke Mannheim). Die Leistung der 1- und 2-Zylinder-Dieselaggregate reichte von etwa 10 kW (14 PS) beim Fix 1 bis zu 16 kW (22 PS) beim Fix 2E. Die Kraftübertragung erfolgte über ein Fendt-Gruppenschaltgetriebe mit serienmäßig sechs Vorwärts- und zwei Rückwärtsgängen, wobei für einige Modelle optional Superkriechgänge verfügbar waren. Ein markantes Merkmal war das geringe Eigengewicht von rund 1.250 kg, was die Maschinen besonders wendig machte. Die Ausstattung umfasste in der Regel eine Getriebezapfwelle, eine Differentialsperre und auf Wunsch die damals moderne Fendt-Blockhydraulik mit Raddruckverstärker zur Erhöhung der Zugkraft. Gegen Ende der 1960er Jahre wurde die Produktion der Fix-Reihe eingestellt, da der Markt für Kleinstschlepper rückläufig war und Fendt sein Portfolio mit Modellen wie dem Farmer 1E nach unten abrundete. {| class="wikitable" | '''Fix''' (1958–1970) |- | valign="top" | * [[Traktorenlexikon: Fendt Fix 1|Fix 1]] 10 kW (14 PS) * [[Traktorenlexikon: Fendt Fix 2|Fix 2]] 13-14 kW (18-19 PS) * {{:Traktorenlexikon: Create|Fix 2 D}} 12 kW (17 PS) * [[Traktorenlexikon: Fendt Fix 2 E|Fix 2 E]] 16 kW (22 PS) * [[Traktorenlexikon: Fendt Fix 16|Fix 16]] 11 kW (16 PS) |} === Farmer === ==== Farmer (erste Serie) ==== [[File:Fendt Farmer 2 (2).jpg|thumb|Fendt Farmer 2 ]] [[File:Fendt Farmer 4 S JM 2023 08 12 5D410499.jpg|thumb|Fendt Farmer 4 S (Haube ab 1968)]] Nachdem Fendt die Weichen in Richtung Zukunft gestellt hat, und 1958 mit dem Kleinschlepper ''Fix'' und dem Großschlepper ''Favorit'' schon den Einstieg in die neue Schlepper-Generation vollzogen hatte, wurde mit der neuen Farmer-Modellreihe die Schlepper-Familie vervollständigt. Hierbei fand die neu gestylte Motorhaube mit integrierten Scheinwerfern Verwendung, und mit einer Leistung ab 25 PS hatte der Fendt Farmer genau die richtige Dimensionierung, um die Gruppe von mittelgroßen Familienbetrieben zu erreichen. Der Farmer wurde von Fendt technisch sehr modern gestaltet, so ist der Farmer 3 S 1966 der erste Fendt-Schlepper, der mit einer ölhydraulischen Kupplung ausgestattet wurde, welche später unter den Namen 'Turbomatik' zum Markenzeichen von Fendt wurde. Der 3 S ist der erste Schlepper dieser Serie, der einen MWM-Vierzylinder-Motor erhält, es ist der leicht gedrosselte Motor aus dem Favorit 3. 1968 erhält er zur DLG-Ausstellung in München die eckige Motorhaube, die die Produktdesigner nunmehr für der Zeit angemessener halten. {| class="wikitable" | '''Farmer''' (1958–1973) |- | valign="top" | {{:Traktorenlexikon: Create|Farmer 1}} 18 kW (25 PS) {{:Traktorenlexikon: Create|Farmer 1 E}} 22 kW (30 PS) {{:Traktorenlexikon: Create|Farmer 1 Z}} 18 kW (25 PS) {{:Traktorenlexikon: Create|Farmer 1 D}} ---- {{:Traktorenlexikon: Create|Farmer 2}} (FW 139, 1960–1967) 25-28 kW (34-38 PS) {{:Traktorenlexikon: Create|Farmer 2 D}} (FW 228, 1961–1967) 20,5-23 kW (28-32 PS) {{:Traktorenlexikon: Create|Farmer 2 DE}} (FW 228, 1968–1971) 25 kW (35 PS) {{:Traktorenlexikon: Create|Farmer 2 E}} (FW 139, 1968–1970) 29 kW (40 PS) {{:Traktorenlexikon: Create|Farmer 2 S/SA}} (FW/FWA 138, 1968–1972) 30 kW (42 PS) {{:Traktorenlexikon: Create|Farmer 2 W}} ? ---- {{:Traktorenlexikon: Create|Farmer 3 S/SA}} (FW 238/1, FWA 238/7) 33 kW (45 PS) ---- {{:Traktorenlexikon: Create|Farmer 4 S/SA}} (FW 258/1, FWA 258/7) 40 kW (55 PS) ---- {{:Traktorenlexikon: Create|Farmer 5 S/SA}} (FW 268/01, FWA 268/21) 48 kW (65 PS) |} ==== Farmer 100 ==== [[File:Fendt Farmer 108 S Traktor (2023).jpg|thumb|Fendt Farmer 108 S]] Die Fendt Farmer 100 Serie wurde Ende der 1960er Jahre eingeführt und bildete bis in die frühen 1980er Jahre das Rückgrat des Fendt-Programms im mittleren Leistungssegment. Sie löste die erfolgreiche Farmer 1- bis 5-Reihe ab und war die erste Baureihe, die das moderne, kantige Erscheinungsbild der Marke mit der markanten, flachen Motorhaube und den eckigen Doppelscheinwerfern im Grill etablierte. Die Serie deckte mit Modellen wie dem Farmer 102 bis zum Farmer 108 ein Leistungsspektrum von etwa 31 kW (42 PS) bis 55 kW (75 PS) ab und war sowohl mit Hinterrad- als auch mit Allradantrieb erhältlich. Technisch setzten diese Schlepper Maßstäbe durch die flächendeckende Einführung der Turbomatik (Flüssigkeitskupplung) in der S-Ausführung, die ein ruckfreies Anfahren und hohen Schutz für Motor und Getriebe bot. Als Antrieb dienten überwiegend wassergekühlte Drei- und Vierzylinder-Dieselmotoren von MWM, die für ihre Sparsamkeit und Zuverlässigkeit bekannt waren. Das Getriebe verfügte in der Regel über ein Gruppenkonzept mit 13 Vorwärts- und 4 Rückwärtsgängen, was eine für die damalige Zeit sehr feine Abstimmung ermöglichte. Ein besonderes Merkmal war die Vielseitigkeit der Serie: Sie war als robuster Ackerschlepper konzipiert, eignete sich aber aufgrund ihrer kompakten Maße und Wendigkeit ebenso für Hof- und Pflegearbeiten. Die Farmer 100 Serie festigte Fendts Ruf als Premiumhersteller und legte die technische Basis für die 1980 folgende, legendäre Farmer 300 Serie. {| class="wikitable" | '''Farmer 100''' (1972–1980) |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 102}} 29 kW (40 PS) * {{:Traktorenlexikon: Create|Farmer 102 S/SA}} 31 kW (42 PS) * {{:Traktorenlexikon: Create|Farmer 102 LS/LSA}} 35 kW (48 PS) * {{:Traktorenlexikon: Create|Farmer 103}} 35 kW (48 PS) * {{:Traktorenlexikon: Create|Farmer 103 S/SA}} 35 kW (48 PS) * {{:Traktorenlexikon: Create|Farmer 103 LS/LSA}} 35 kW (48 PS) * {{:Traktorenlexikon: Create|Farmer 104 S/SA}} 40 kW (55/60 PS) * {{:Traktorenlexikon: Create|Farmer 104 LS/LSA}} 40 kW (55 PS) * {{:Traktorenlexikon: Create|Farmer 105}} 44kW (60 PS) * {{:Traktorenlexikon: Create|Farmer 105 S/SA}} 55,2 kW (75 PS) * {{:Traktorenlexikon: Create|Farmer 105 LS/LSA}} 44kW (60 PS) * {{:Traktorenlexikon: Create|Farmer 106 S/SA}} 48 kW (65 PS) * {{:Traktorenlexikon: Create|Farmer 106 LS/LSA}} 48 kW (65 PS) * {{:Traktorenlexikon: Create|Farmer 108 S/SA}} 55 kW (75 PS) * {{:Traktorenlexikon: Create|Farmer 108 LS/LSA}} 55 kW (75 PS) |} ==== Farmer 200 ==== [[File:Agricultural Liechtenstein license plate old style.jpg|thumb|Fendt Farmer 240 S]] Die Fendt Farmer 200 Serie wurde ab 1974 als spezialisierte Baureihe für Betriebe mit begrenzten Platzverhältnissen sowie für den Einsatz im Grünland und im Nebenerwerb eingeführt. Sie besetzte die Nische zwischen den Kleinschleppern der Fix-Reihe und den größeren Modellen der Farmer 300 Serie und zeichnete sich durch eine besonders kompakte Bauweise, eine niedrige Gesamthöhe und eine hohe Wendigkeit aus. Technisch unterschieden sich die frühen Modelle, wie der Farmer 200 S bis 205 P, durch den Einsatz von luftgekühlten Deutz-Motoren, was eine kompaktere Konstruktion ermöglichte. Im Gegensatz zur größeren 300er-Serie wurde in der 200er-Reihe zunächst auf die Turbomatik verzichtet, um Bauraum und Gewicht zu sparen, wobei das Getriebekonzept speziell auf die Anforderungen im alpinen Raum und in Sonderkulturen abgestimmt war. Die Serie unterteilte sich in verschiedene spezialisierte Ausführungen, darunter die Standard-Versionen für den allgemeinen landwirtschaftlichen Einsatz sowie die Schmalspur-Varianten V und P für den Wein- und Obstbau. In den 1980er und 1990er Jahren folgte eine kontinuierliche Modernisierung, bei der die Motorhauben optisch an das Design der größeren Baureihen angepasst wurden und vermehrt wassergekühlte Aggregate Einzug hielten. Trotz der geringeren Abmessungen bot die 200er Serie eine vollwertige Ausstattung mit Zapfwellen und leistungsfähiger Hydraulik, was sie zum idealen Werkzeug für spezialisierte Betriebe machte. Im Jahr 2003 endete die Ära der klassischen Farmer 200 Serie mit der Einführung des Fendt 200 Vario, der als weltweit erster Spezialtraktor mit stufenlosem Getriebe die Tradition der kompakten Hochleistungsschlepper fortsetzte. {| class="wikitable" | '''Farmer 200''' (1974–2003) |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 200 S/SA}} 26 kW (35 PS) * {{:Traktorenlexikon: Create|Farmer 201 S}} 31/33 kW (42/45 PS) * {{:Traktorenlexikon: Create|Farmer 204 P}} 48 kW (65 PS) * {{:Traktorenlexikon: Create|Farmer 206 S}} * {{:Traktorenlexikon: Create|Farmer 207 S}} * {{:Traktorenlexikon: Create|Farmer 208 S}} 63 kW (86 PS) * {{:Traktorenlexikon: Create|Farmer 209 S}} * {{:Traktorenlexikon: Create|Farmer 240 S}} * {{:Traktorenlexikon: Create|Farmer 250 S/SA}} * {{:Traktorenlexikon: Create|Farmer 260 P}} * {{:Traktorenlexikon: Create|Farmer 260 S/SA}} 44 kW (60 PS) * {{:Traktorenlexikon: Create|Farmer 275 S/SA}} * {{:Traktorenlexikon: Create|Farmer 280 S}} |} ===Schmalspur-Schlepper=== [[File:Fendt Farmer 203 P II Traktor.jpg|thumb|Fendt Farmer 203 P]] Die Fendt Farmer Schmalspurtraktoren wurden speziell für den Einsatz in Sonderkulturen wie Wein-, Obst- und Hopfenbau entwickelt, wo extrem schmale Fahrgassen eine minimale Außenbreite der Maschine erfordern. Die Baureihe wurde ab den 1970er Jahren parallel zur Standard-200er-Serie geführt und durch die Kennbuchstaben V (Vineyard/Weinbau) und P (Plantage/Obstbau) unterschieden. Während die V-Modelle mit einer Außenbreite von teilweise unter einem Meter für engste Rebanlagen optimiert waren, boten die etwas breiteren P-Modelle mehr Standfestigkeit für Arbeiten in Obstplantagen oder im Hopfenanbau. Technisch basierten die Schmalspurschlepper zunächst auf luftgekühlten Deutz-Motoren, um die kompakten Abmessungen und die erforderliche Wendigkeit zu realisieren. Ein wesentliches Merkmal war der extrem enge Radstand und die spezielle Vorderachskonstruktion, die einen hohen Lenkeinschlag ermöglichte. Trotz der geringen Größe waren die Maschinen voll ausgestattete Traktoren mit leistungsfähiger Hydraulik, mehreren Zapfwellengeschwindigkeiten und – in späteren Generationen – komfortablen, schmalen Kabinen inklusive Filteranlagen für den Pflanzenschutz. In den 1990er Jahren wurden auch in diesem Segment wassergekühlte Motoren und modernere Getriebe zum Standard, bevor die Baureihe 2009 durch den 200 Vario V/F/P ersetzt wurde, der als erster Schmalspurtraktor der Welt mit einem stufenlosen Getriebe die Marktführerschaft in diesem spezialisierten Segment festigte. {| class="wikitable" | '''Farmer 200 V/F/P''' |- | valign="top" | * {{:Traktorenlexikon: Create|FLS 237}} * {{:Traktorenlexikon: Create|Farmer 2 W}} * {{:Traktorenlexikon: Create|Farmer 200 V}} * {{:Traktorenlexikon: Create|Farmer 203 V/VA}} * {{:Traktorenlexikon: Create|Farmer 203 V/VA-II}} * {{:Traktorenlexikon: Create|Farmer 204 V/VA}} * {{:Traktorenlexikon: Create|Farmer 250 V/VA}} * {{:Traktorenlexikon: Create|Farmer 260 V/VA}} * {{:Traktorenlexikon: Create|Farmer 270 V/VA}} * {{:Traktorenlexikon: Create|Farmer 275 V/VA}} * {{:Traktorenlexikon: Create|Farmer 280 VA}} * {{:Traktorenlexikon: Create|Farmer 203 P/PA}} * {{:Traktorenlexikon: Create|Farmer 203 P/PA-II}} * {{:Traktorenlexikon: Create|Farmer 204 P}} * {{:Traktorenlexikon: Create|Farmer 205 P/PA}} * {{:Traktorenlexikon: Create|Farmer 260 P/PA}} * {{:Traktorenlexikon: Create|Farmer 270 P/PA}} * {{:Traktorenlexikon: Create|Farmer 280 P/PA}} * {{:Traktorenlexikon: Create|Farmer 206 F/FA}} * {{:Traktorenlexikon: Create|Farmer 207 F/FA}} * {{:Traktorenlexikon: Create|Farmer 208 V/VA}} * {{:Traktorenlexikon: Create|Farmer 208 FA}} * {{:Traktorenlexikon: Create|Farmer 208 PA}} * {{:Traktorenlexikon: Create|Farmer 209 VA}} * {{:Traktorenlexikon: Create|Farmer 209 FA}} * {{:Traktorenlexikon: Create|Farmer 209 PA}} |} ====Farmer 300==== [[File:Fendt Farmer 307LSA Turbomatik.jpg|thumb|Fendt Farmer 307LSA Turbomatik]] Die Fendt Farmer 300 Serie gilt als eine der erfolgreichsten und prägendsten Traktorenbaureihen der europäischen Landtechnikgeschichte. Eingeführt im Jahr 1980, blieb die Baureihe in verschiedenen Evolutionsstufen über einen Zeitraum von fast drei Jahrzehnten in Produktion und deckte als vielseitiger Allrounder das mittlere Leistungssegment ab. Ein zentrales technisches Merkmal war die Integration der Fendt-Turbomatik, einer Flüssigkeitskupplung, die ein verschleißfreies Anfahren ermöglichte und den Antriebsstrang schonte. In der ersten Generation, den LS- und LSA-Modellen, wurden überwiegend wassergekühlte 3-, 4- und 6-Zylinder-Motoren von MWM verbaut, die ein Leistungsspektrum von etwa 43 kW (58 PS) bis 92 kW (125 PS) abdeckten. Im Laufe der Jahre wurde die Serie kontinuierlich modernisiert, wobei insbesondere die Einführung der 40-km/h-Varianten mit serienmäßiger Vierradbremse und gefederter Vorderachse Maßstäbe in der Mittelklasse setzte. Mit der umfassenden Modellpflege im Jahr 1993 erhielten die Schlepper eine deutlich leisere und ergonomisch optimierte Kabine sowie eine abgerundete Motorhaube. Das Getriebe wurde stetig weiterentwickelt, von den klassischen 21/6-Gang-Varianten bis hin zu Versionen mit elektrohydraulischer Wendeschaltung. In der finalen Phase der Baureihe, den C- und Ci-Modellen ab Ende der 1990er Jahre, kamen verstärkt Motoren von Deutz zum Einsatz. Aufgrund ihrer extremen Robustheit, der hohen Wertbeständigkeit und der einfachen Handhabung ist die Farmer 300 Serie bis heute auf vielen Betrieben im aktiven Einsatz. Erst im Jahr 2006 wurde die Baureihe durch den technologisch völlig neu konzipierten Fendt 300 Vario mit stufenlosem Antrieb abgelöst. [[File:Fendt Farmer 305 LS.jpg|thumb|Fendt Farmer 305 LS]] Die Bezeichnung '''LS''' steht für „Luxus-Standard“ und kennzeichnete innerhalb der Farmer 300-Serie die Modelle mit Hinterradantrieb. Diese Ausführung wurde vor allem für Betriebe konzipiert, die einen wendigen und leichten Schlepper für Pflegearbeiten, leichte Transportaufgaben oder den Einsatz im Grünland benötigten, bei denen die zusätzliche Zugkraft eines Allradantriebs nicht zwingend erforderlich war. Trotz des fehlenden Allradantriebs bot die LS-Variante den vollen Komfort der neu entwickelten, gummigelagerten Sicherheitskabine, die den Lärmpegel für den Fahrer massiv senkte. Technisch waren diese Maschinen mit der bewährten Turbomatik-Flüssigkeitskupplung und dem feingestuften 21/6-Gang-Getriebe ausgestattet. Durch das geringere Eigengewicht an der Vorderachse und den größeren Einschlagwinkel der Vorderräder war die LS-Version in ihrer Manövrierfähigkeit den Allradmodellen überlegen, was sie zu einer beliebten Wahl für Hof- und Stallarbeiten machte. {| class="wikitable" | '''Farmer 300 LS''' (1980–1984) |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 303 LS}} 38 kW (52 PS) * {{:Traktorenlexikon: Create|Farmer 304 LS}} 43 kW (58 PS) 1982-1989 * {{:Traktorenlexikon: Create|Farmer 304 LS/Turbo}} 1990-1998 * {{:Traktorenlexikon: Create|Farmer 305 LS}} 46 kW (63 PS) * {{:Traktorenlexikon: Create|Farmer 306 LS}} 51 kW (70 PS) * {{:Traktorenlexikon: Create|Farmer 307 LS}} 51 kW (70 PS) 1988-1998 * {{:Traktorenlexikon: Create|Farmer 308 LS}} 57 kW (78 PS) * {{:Traktorenlexikon: Create|Farmer 309 LS}} 63 kW (86 PS) |} [[File:Fendt Farmer 307 LSA Turbomatik.jpg|thumb|Fendt Farmer 307 LSA Turbomatik]] Das Kürzel '''LSA''' steht für „Luxus-Standard-Allrad“ und markierte den technologischen Standard für leistungsstarke Universaltraktoren ab den 1980er Jahren. Im Gegensatz zur LS-Variante verfügten diese Modelle über einen optimierten Allradantrieb, der durch eine zentrale Gelenkwelle und ein unter Last zuschaltbares Lamellendifferential an der Vorderachse bestach. Die LSA-Modelle wurden schnell zum Rückgrat der mittelständischen Landwirtschaft, da sie die Motorleistung der 3-, 4- und 6-Zylinder-MWM-Motoren auch unter schwierigen Bedingungen effizient in Zugkraft umsetzen konnten. Neben der markanten Turbomatik und der komfortablen Kabine waren die LSA-Versionen häufig die Vorreiter für technische Neuerungen wie die 40-km/h-Zulassung und die elektronische Hubwerksregelung (EHR). Aufgrund ihrer Vielseitigkeit und der hohen Standfestigkeit bei schweren Acker- und Frontladerarbeiten entwickelten sich die LSA-Modelle zu den meistverkauften Varianten der Farmer-Reihe und gelten heute als Inbegriff des klassischen Fendt-Schleppers dieser Ära. {| class="wikitable" | '''Farmer 300 LSA''' (1984–1990, 1991–1993) |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 303 LSA}} 38 kW (52 PS) * {{:Traktorenlexikon: Create|Farmer 304 LSA}} 43 kW (58 PS) 1982-1989 * {{:Traktorenlexikon: Create|Farmer 304 LSA/Turbo}} 1990-1998 * {{:Traktorenlexikon: Create|Farmer 305 LSA}} 46 kW (63 PS) * {{:Traktorenlexikon: Create|Farmer 306 LSA}} 51 kW (70 PS) * {{:Traktorenlexikon: Create|Farmer 307 LSA}} 51 kW (70 PS) 1988-1998 * {{:Traktorenlexikon: Create|Farmer 308 LSA}} 57 kW (78 PS) * {{:Traktorenlexikon: Create|Farmer 309 LSA}} 63/66 kW (86/90 PS) * {{:Traktorenlexikon: Create|Farmer 310 LSA}} 68 kW (92 PS) * {{:Traktorenlexikon: Create|Farmer 311 LSA}} 74 kW (100 PS) * {{:Traktorenlexikon: Create|Farmer 312 LSA}} 85 kW (115 PS) |} [[File:Fendt 308.jpg|thumb|Fendt Farmer 308]] In der Periode von 1993 bis 1996 erlebte die Fendt '''Farmer 300''' Serie (modellspezifisch die Typen 307 bis 312) ihre technisch und optisch ausgereifteste Phase, bevor die Baureihe in die C- und Ci-Generationen aufgeteilt wurde. Diese Jahre markierten den Höhepunkt der klassischen 300er-Reihe, die nun serienmäßig das neue, abgerundete Design der Motorhaube und die markante, nach oben gezogene Frontscheibe erhielt. Technisch setzten diese Modelle weiterhin auf die bewährten wassergekühlten MWM-Motoren, die für ihre Langlebigkeit bekannt waren. Ein entscheidender Fortschritt dieser Jahre war die Einführung der elektrohydraulischen Wendeschaltung (EHS) und der verbesserten Komfortkabine, die mit einem Geräuschpegel von nur 72 dB(A) zu den leisesten auf dem Markt gehörte. Zudem wurde in diesem Zeitraum die gefederte Vorderachse für die größeren Modelle verfügbar, was in Kombination mit der 40-km/h-Zulassung und der echten Vierradbremse den Fahrkomfort erheblich steigerte. Diese Generation gilt heute als besonders begehrt, da sie die robuste, weitgehend mechanische Technik der Turbomatik-Ära mit einem modernen Bedienkomfort vereinte, der bereits viele Merkmale der späteren High-Tech-Schlepper vorwegnahm. {| class="wikitable" | '''Farmer 300''' (1993–1996) |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 307}} 55 kW (75 PS) * {{:Traktorenlexikon: Create|Farmer 308}} 63 kW (86 PS) * {{:Traktorenlexikon: Create|Farmer 309}} 70 kW (95 PS) * {{:Traktorenlexikon: Create|Farmer 310}} 77 kW (105 PS) * {{:Traktorenlexikon: Create|Farmer 311}} 85 kW (115 PS) * {{:Traktorenlexikon: Create|Farmer 312}} 92 KW (125PS) |} [[File:Fendt Farmer 308C.jpg|thumb|Fendt Farmer 308C]] Die '''Farmer 300 C'''-Serie wurde Ende der 1990er Jahre als technisch vereinfachte, aber moderne Alternative eingeführt. Sie sollte die Lücke schließen, die durch die zunehmende Komplexität der größeren Baureihen entstand. Ein markanter Unterschied war der Wechsel der Motorenlieferanten: Statt MWM kamen nun kompakte Deutz-Motoren zum Einsatz. Die C-Serie verzichtete auf einige elektronische Features der High-End-Modelle, bot jedoch mit der 40-km/h-Zulassung und einer kompakten Bauweise ideale Voraussetzungen für Grünlandbetriebe und Hofarbeiten. {| class="wikitable" | '''Farmer 300 C''' |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 307 C}} 55 kW (75 PS) * {{:Traktorenlexikon: Create|Farmer 308 C}} 63 kW (86 PS) * {{:Traktorenlexikon: Create|Farmer 309 C}} 70 kW (95 PS) |} [[Datei:Fendt 308 CI.jpg|thumb|Farmer 308 Ci]] Die '''Farmer 300 Ci'''-Serie stellte ab 2003 die finale Evolutionsstufe der Farmer-Reihe mit klassischem Schaltgetriebe dar. Sie basierte auf der C-Serie, wurde jedoch technisch nochmals verfeinert, um strengere Abgasnormen zu erfüllen und den Bedienkomfort (z. B. durch eine verbesserte Kabinenergonomie) zu steigern. Mit Modellen wie dem 309 Ci blieb Fendt der mechanischen Kraftübertragung treu, während größere Serien bereits auf Vario-Getriebe umgestellt wurden. Die Produktion endete 2008, womit die Ära der „Farmer“-Baureihe nach über 50 Jahren zugunsten der rein stufenlosen 300 Vario-Modelle abgeschlossen wurde. {| class="wikitable" | '''Farmer 300 Ci''' |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 307 Ci}} 68 kW (92 PS) * {{:Traktorenlexikon: Create|Farmer 308 Ci}} 73 kW (98 PS) * {{:Traktorenlexikon: Create|Farmer 309 Ci}} 83 kW (112PS) |} ==== Farmer 400 Vario==== [[File:Veghel opendag abemec 18 12 05 (71).JPG|thumb|Fendt Farmer 411 Vario]] Die Fendt Farmer 400 Vario Serie wurde im Jahr 1999 als kompakte Ergänzung zur größeren 700er-Serie eingeführt und markierte den Einzug der stufenlosen Getriebetechnik in das Segment der mittleren Kompaktschlepper. Sie löste die leistungsstärkeren Modelle der Farmer 300 Serie ab und umfasste die Typen 409, 410, 411 und 412 Vario, die einen Leistungsbereich von 70 kW (95 PS) bis 92 kW (125 PS) abdeckten. Damit war die 400er-Reihe die erste Baureihe unterhalb der 100-PS-Grenze, die weltweit mit einem stufenlosen Antrieb ausgestattet wurde. Das technische Herzstück war das ML-75-Vario-Getriebe, das ein vollkommen ruckfreies Fahren und Arbeiten von 0,02 bis 50 km/h ermöglichte. Als Kraftquelle dienten wassergekühlte 4-Zylinder-Turbomotoren von Deutz mit 3,8 Litern Hubraum und Ladeluftkühlung, die sich durch eine kompakte Bauweise und hohe Spritzigkeit auszeichneten. Trotz der geringen Abmessungen verfügte die Serie über die vollwertige High-Tech-Ausstattung der größeren Modelle, einschließlich des Varioterminals zur Steuerung von Hydraulik und Getriebemanagement sowie der optionalen Variotronic für das Vorgewendemanagement. Ein besonderes Merkmal der Farmer 400 Vario Serie war ihre enorme Wendigkeit bei gleichzeitig hohem Komfortniveau, das durch die serienmäßige Vorderachsfederung und eine komfortable Kabinenfederung erreicht wurde. Durch den kurzen Radstand und das relativ geringe Eigengewicht war sie prädestiniert für Frontladerarbeiten, den Einsatz im Grünland und als Pflegeschlepper in Ackerbaubetrieben. Mit der Überarbeitung im Jahr 2006 entfiel der Name „Farmer“, und die Serie wurde als Fendt 400 Vario (mit Leistungen bis 155 PS) weitergeführt, bis sie schließlich durch die 500 Vario Serie ersetzt wurde. Aufgrund ihrer Zuverlässigkeit und universellen Einsetzbarkeit gilt die 400er-Reihe heute als einer der wertstabilsten Gebrauchtschlepper ihrer Klasse. {| class="wikitable" | '''Farmer 400 Vario''' |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 307 Ci}} 68 kW (92 PS) * {{:Traktorenlexikon: Create|Farmer 308 Ci}} 73 kW (98 PS) * {{:Traktorenlexikon: Create|Farmer 309 Ci}} 83 kW (112PS) |} === Favorit === Die Fendt Favorit Serie bildete über Jahrzehnte das technologische Flaggschiff des Allgäuer Herstellers und repräsentierte das Segment der schweren Großtraktoren. Die Baureihe wurde bereits in den 1950er Jahren als leistungsstarke Ergänzung zu den kleineren Dieselross- und Farmer-Modellen eingeführt und durchlief eine bemerkenswerte technische Evolution von der mechanischen Ära bis hin zur Hochleistungselektronik. Während die frühen Generationen wie der Favorit 1, 2 und 3 ab 1958 vor allem durch Robustheit und Motoren von MWM im Bereich von 30 bis 40 kW (40–55 PS) überzeugten, setzten die späteren Generationen völlig neue Branchenstandards. Ein Meilenstein war die 1976 eingeführte Serie 600 (LS/LSA), die mit Leistungen bis zu 136 kW (185 PS) und der serienmäßigen Turbomatik-Flüssigkeitskupplung die Oberklasse dominierte. Diese Sechszylindermaschinen galten aufgrund ihrer massiven Bauweise und enormen Zugkraft als unzerstörbar. In den 1990er Jahren folgte mit der Baureihe 800 und 900 der Sprung in die moderne Hochleistungstechnik. Besonders der 1993 vorgestellte Favorit 800 und der 1996 erschienene Favorit 900 Vario schrieben Technikgeschichte; letzterer war der weltweit erste Großtraktor mit dem stufenlosen Vario-Getriebe, das die Art der Kraftübertragung in der Landwirtschaft revolutionierte. In ihrer finalen Phase zeichnete sich die Favorit-Serie durch Merkmale wie die gefederte Vorderachse, Kabinenfederung und eine umfassende elektronische Steuerung aus. Die Modelle, allen voran der legendäre 926 Vario, festigten den Ruf von Fendt als Premiummarke im Profisegment. Mit der vollständigen Umstellung des gesamten Portfolios auf den stufenlosen Antrieb entfiel die Zusatzbezeichnung „Favorit“ schließlich in den frühen 2000er Jahren, wobei die Leistungsmerkmale und die Positionierung als High-End-Großtraktor direkt in den heutigen Fendt 800 und 900 Vario Serien fortgeführt wurden. ==== Favorit (erste Reihe) ==== [[File:Oldtimerumzug Aidenbach 2013-08-18 - Fendt Favorit 3.JPG|thumb|Fendt Favorit 3]] [[File:Fendt Favorit 4S 127PS 1967.jpg|thumb|Fendt Favorit 4S]] Die erste Serie der Fendt Favorit-Baureihe markierte ab dem Jahr 1958 den Einstieg des Herstellers in das Segment der schweren Ackerschlepper. Sie wurde als leistungsstarke Ergänzung zu den kleineren Dieselross- und Farmer-Modellen konzipiert, um den steigenden Anforderungen größerer landwirtschaftlicher Betriebe gerecht zu werden. Die Baureihe startete mit dem Favorit 1 (Typ FW 140), der von einem wassergekühlten MWM-Dreizylinder-Dieselmotor mit einer Leistung von 29 kW (40 PS) angetrieben wurde. Kurz darauf folgten die Modelle Favorit 2 mit 33 kW (45 PS) und schließlich der Favorit 3, der ab 1964 mit einem Vierzylindermotor und einer Leistung von bis zu 38 kW (52 PS) die Baureihe nach oben abrundete. Ein technisches Kernstück dieser ersten Generation war das ZF-Gruppengetriebe (Zahnradfabrik Friedrichshafen), das in der Regel über zehn Vorwärts- und zwei Rückwärtsgänge verfügte und eine präzise Abstimmung der Arbeitsgeschwindigkeit ermöglichte. Das Design der ersten Favorit-Schlepper war durch eine markante, langgezogene Motorhaube und die damals für Fendt typische grüne Lackierung mit roten Felgen geprägt. Zur Ausstattung gehörten bereits eine moderne Motorzapfwelle sowie eine leistungsfähige Hydraulik, die den Einsatz schwerer Anbaugeräte erlaubte. Besonders der Favorit 3 setzte mit seiner robusten Bauweise und der optionalen Allradversion Maßstäbe in Sachen Zugkraft und Zuverlässigkeit. Diese erste Serie legte den Grundstein für den exzellenten Ruf der Favorit-Reihe als Hochleistungstraktoren und ebnete den Weg für die späteren Sechszylinder-Großschlepper der 600er Serie. {| class="wikitable" | '''Favorit''' (1958–1972) |- | valign="top" | * {{:Traktorenlexikon: Create|Favorit 1}} (FW 140/1, FW 140/3) 29 kW (40 PS) * {{:Traktorenlexikon: Create|Favorit 2}} (FW 150/1, FW 150/3) 33 kW (46 PS) * {{:Traktorenlexikon: Create|Favorit 3}} (FW 150/11, FWA 150/7) 38-40 kW (52-55 PS) * {{:Traktorenlexikon: Create|Favorit 3 S/SA}} (FW 165/1, FWA 165/7) 45,4 kW (62 PS) * {{:Traktorenlexikon: Create|Favorit 4}} (FW 180/3, FWA 180/7) 59 kW (80 PS) * {{:Traktorenlexikon: Create|Favorit 4 S/SA}} (FW 180/3, FWA 180/7) 66 kW (90 PS) * {{:Traktorenlexikon: Create|Favorit 10 S/SA}} (FW 181/01, FWA 181/21) 58 kW (80 PS) * {{:Traktorenlexikon: Create|Favorit 11 S/SA}} (FW 182/01, FWA 182/21) 69 kW (95 PS) * {{:Traktorenlexikon: Create|Favorit 12 SA}} (FWA 183/21) 80 kW (110 PS) |} ==== Favorit 500 ==== [[File:Fendt Favorit 515 C Turboshift (2023).jpg|thumb|Fendt Favorit 515 C]] Die Fendt Favorit 500 Serie wurde 1993 eingeführt und gilt bis heute als eine der fortschrittlichsten und vielseitigsten Traktorengenerationen, die Fendt je produziert hat. Sie schloss die Lücke zwischen der kompakten Farmer 300 Serie und den Großschleppern der Favorit 800 Serie. Mit einem Leistungsspektrum von 70 kW (95 PS) beim Favorit 509 C bis zu 110 kW (150 PS) beim Favorit 515 C vereinte die Baureihe erstmals die Wendigkeit eines Mittelklasseschleppers mit der Technik und Leistungsfähigkeit der Oberklasse. Technisch basierte die Serie auf wassergekühlten 4- und 6-Zylinder-Turbomotoren von MWM, die für ihre hohe Durchzugskraft bekannt waren. Ein herausragendes Merkmal war das Turboshift-Getriebe, das in Kombination mit der bewährten Turbomatik-Flüssigkeitskupplung ein extrem komfortables Fahren ermöglichte. Das Getriebe verfügte über vier Lastschaltstufen und eine elektrohydraulische Wendeschaltung, was insgesamt 44 Vorwärts- und 44 Rückwärtsgänge ergab. Damit waren die Traktoren bereits serienmäßig für eine Höchstgeschwindigkeit von 50 km/h ausgelegt. Zudem war die Serie 500 ein Pionier in Sachen Federungskomfort: Die Kombination aus gefederter Vorderachse und einer dreifach gummigelagerten Komfortkabine setzte neue Maßstäbe in der Branche. Die Kabine selbst bot eine moderne Ergonomie mit einer übersichtlichen Seitenkonsole, auf der viele Funktionen elektrohydraulisch gesteuert werden konnten. Dank der leistungsstarken Hydraulikanlage und der hohen Hubkraft waren die 500er-Modelle sowohl für schwere Ackerarbeiten als auch für Transport- und Frontladeraufgaben prädestiniert. Aufgrund ihrer enormen Vielseitigkeit und der robusten, langlebigen Technik erzielen gebrauchte Modelle der Favorit 500 Serie heute noch außergewöhnlich hohe Wiederverkaufswerte. Die Baureihe wurde Ende der 1990er Jahre schließlich durch die ersten stufenlosen Modelle der 700 Vario Serie ersetzt. {| class="wikitable" | '''Favorit 500''' (1993–2000) |- | valign="top" | * {{:Traktorenlexikon: Create|Favorit 509 C}} 70 kW (95 PS) * {{:Traktorenlexikon: Create|Favorit 510 C}} 77 kW (105 PS) * {{:Traktorenlexikon: Create|Favorit 511 C}} 85 kW (115 PS) * {{:Traktorenlexikon: Create|Favorit 512 C}} 93 kW (125 PS) * {{:Traktorenlexikon: Create|Favorit 514 C}} 103 kW (140 PS) * {{:Traktorenlexikon: Create|Favorit 515 C}} (FWA 515) 110 kW (150 PS) |} ==== Favorit 600 ==== [[File:Fendt Favorit 614 LS Turbomatik.jpg|thumb|Fendt Favorit 614 LS Turbomatik]] Die Fendt Favorit 600 Serie (insbesondere die LS- und LSA-Modelle) war die dominierende Großtraktorenbaureihe von Fendt in den 1970er, 80er und frühen 90er Jahren. Sie festigte den Ruf der Marke als Premiumhersteller im schweren Leistungssegment und war für ihre enorme Zugkraft sowie die hohe technische Zuverlässigkeit bekannt. Die Baureihe umfasste eine Vielzahl von Modellen, angefangen beim 600 LS (85 PS) bis hin zum legendären Spitzenmodell 615 LSA, das in seiner letzten Ausbaustufe bis zu 136 kW (185 PS) leistete. Eine technische Besonderheit und ein Alleinstellungsmerkmal in dieser Klasse war die serienmäßige Turbomatik-Flüssigkeitskupplung, die ein verschleißfreies Anfahren ermöglichte und den gesamten Antriebsstrang vor Überlastung schützte. Technisch setzten die 600er-Modelle konsequent auf wassergekühlte Sechszylinder-Dieselmotoren von MWM, die ab Mitte der 1980er Jahre in den größeren Modellen serienmäßig mit Turboladern ausgestattet waren. Die Kraftübertragung erfolgte über robuste ZF-Getriebe, die im Laufe der Produktion von 12/5-Gängen bis hin zu hochmodernen 20/9-Gang-Vollsynchrongetrieben weiterentwickelt wurden. Diese ermöglichten bereits frühzeitig eine Höchstgeschwindigkeit von 40 km/h. Mit der Einführung der LSA-Ausführung (Luxus-Standard-Allrad) wurde zudem die Kabine grundlegend modernisiert; die gummigelagerte Sicherheitskabine bot eine für die damalige Zeit exzellente Schalldämmung und hohen Bedienkomfort. Ein technologischer Exot der Serie war der Favorit 626 LS, der mit über 250 PS und einer speziellen Transaxle-Bauweise für schwerste Bodenbearbeitung konzipiert wurde. Aufgrund ihrer massiven Blockbauweise und der weitgehenden Abwesenheit störanfälliger Elektronik gelten die Traktoren der Favorit 600 Serie bis heute als gesuchte Klassiker, die oft noch im harten Praxiseinsatz zu finden sind. {| class="wikitable" | '''Favorit 600 S/SA''' (1972–1976) || '''Favorit 600 SL''' (1976–1979) || '''Favorit 600 LS''' (1978-1984) || '''Favorit 600 LSA''' (1984-1993) |- | valign="top" | <!-- 600 S/SA --> * {{:Traktorenlexikon: Create|Favorit 610 S}} (FW 181/01) 63 kW (85 PS) * {{:Traktorenlexikon: Create|Favorit 610 SA}} (FWA 181/21) 63 kW (85 PS) * {{:Traktorenlexikon: Create|Favorit 611 S}} (FW 182/01) 77 kW (105 PS) * {{:Traktorenlexikon: Create|Favorit 611 SA}} (FWA 182/21) 77 kW (105 PS) * {{:Traktorenlexikon: Create|Favorit 612 S}} ''eigentlich SA?'' 88 kW (120 PS) * {{:Traktorenlexikon: Create|Favorit 612 SA}} (FWA 184/21) 88 kW (120 PS) * {{:Traktorenlexikon: Create|Favorit 614 SA}} (FWA 184/24) 99 kW (135 PS) | valign="top" | <!-- 600 SL --> * {{:Traktorenlexikon: Create|Favorit 610 SL}} 66 kW (90 PS) * {{:Traktorenlexikon: Create|Favorit 611 SL}} 77 kW (105 PS) * {{:Traktorenlexikon: Create|Favorit 612 SL}} 88 kW (120 PS) * {{:Traktorenlexikon: Create|Favorit 614 SL}} 99 kW (135 PS) * {{:Traktorenlexikon: Create|Favorit 615 SL}} 110 kW (150 PS) | valign="top" | <!-- 600 LS --> * {{:Traktorenlexikon: Create|Favorit 600 LS}} (FW(A) 280) 63 kW (85 PS) * {{:Traktorenlexikon: Create|Favorit 610 LS}} (FW(A) 281) 70 kW (95 PS) * {{:Traktorenlexikon: Create|Favorit 611 LS}} (FWA 282)(105PS) * {{:Traktorenlexikon: Create|Favorit 612 LS}} (FWA 283) * {{:Traktorenlexikon: Create|Favorit 614 LS}} (FWA 284) 99 kW (135 PS) * {{:Traktorenlexikon: Create|Favorit 615 LS}} (FWA 285) | valign="top" | <!-- 600 LSA --> * {{:Traktorenlexikon: Create|Favorit 611 LSA}} (FWA 382) * {{:Traktorenlexikon: Create|Favorit 612 LSA}} (FWA 383) 88-107 kW (120-145 PS) * {{:Traktorenlexikon: Create|Favorit 614 LSA}} (FWA 384) 99-121 kW (135-165 PS) * {{:Traktorenlexikon: Create|Favorit 615 LSA}} (FWA 385) ---- * {{:Traktorenlexikon: Create|Favorit 620 LS}} (FWA 291) <!-- Prototyp --> 135 kW (185 PS) * {{:Traktorenlexikon: Create|Favorit 622 LS/LSA}} (FWA 291) 155 kW (211 PS) * {{:Traktorenlexikon: Create|Favorit 626 LS/LSA}} (FWA 292) 185 kW (252 PS) |} ==== Favorit 700 Vario ==== [[Datei:Fendt 716 vario.JPG|miniatur|[[Traktorenlexikon: Fendt Favorit 716 Vario|Favorit 716 Vario]]]] Die Fendt Favorit 700 Vario Serie markierte bei ihrer Einführung im Jahr 1998 einen technologischen Wendepunkt, da sie die erste Mittelklasse-Baureihe weltweit war, die mit dem stufenlosen Vario-Getriebe ausgestattet wurde. Sie löste die erfolgreiche Favorit 500 Serie ab und übernahm deren Konzept des vielseitigen Hochleistungstraktors, ergänzt um die revolutionäre Antriebstechnik des größeren Favorit 900. Die Baureihe umfasste Modelle vom 711 Vario mit 85 kW (115 PS) bis zum Topmodell 716 Vario, das eine Maximalleistung von 118 kW (160 PS) erreichte. Technisch setzten die 700er-Modelle auf wassergekühlte 6-Zylinder-Turbomotoren von Deutz mit einem Hubraum von 6,1 Litern. Das Herzstück war jedoch das ML-160-Vario-Getriebe, das ein vollkommen stufenloses Fahren von 0 bis 50 km/h ermöglichte, ohne dass Schaltvorgänge unterbrochen werden mussten. Ein weiteres Novum war das Varioterminal, über welches der Fahrer die Hydraulik, die Zapfwelle und das Getriebemanagement zentral steuern konnte. In Kombination mit dem Vorgewendemanagement „Variotronic“ bot die Serie einen Automatisierungsgrad, der zuvor nur in der absoluten Oberklasse zu finden war. Der Fahrkomfort wurde durch die Kombination aus gefederter Vorderachse und einer pneumatischen Kabinenfederung auf ein neues Niveau gehoben. Dank ihrer kompakten Abmessungen bei gleichzeitig hoher Motorleistung entwickelte sich die 700er Serie schnell zum Standard für Lohnunternehmer und größere landwirtschaftliche Betriebe. Mit der Überarbeitung zur zweiten Generation und dem Wegfall der Bezeichnung „Favorit“ in der Namensgebung der frühen 2000er Jahre wurde die Serie als Fendt 700 Vario fortgeführt. Sie gilt bis heute als eine der meistverkauften und wertstabilsten Baureihen im Fendt-Portfolio. {| class="wikitable" |- | valign="top" | * {{:Traktorenlexikon: Create|Favorit 711 Vario}} (FWA 711) 85 kW (115 PS) * {{:Traktorenlexikon: Create|Favorit 712 Vario}} (FWA 712) 92 kW (125 PS) * {{:Traktorenlexikon: Create|Favorit 714 Vario}} (FWA 714) 103 kW (140 PS) * {{:Traktorenlexikon: Create|Favorit 716 Vario}} (FWA 716) 118 kW (160 PS) |} ==== Favorit 800 ==== [[File:Fendt Favorit 816.jpg|thumb|Fendt Favorit 816]] Die Fendt Favorit 800 Serie wurde 1993 als neue Hochleistungsklasse oberhalb der 600er-Serie eingeführt und markierte für Fendt den Übergang zum modernen Großtraktoren-Design. Die Baureihe umfasste die Modelle 816, 818, 822 und 824 mit einem Leistungsspektrum von 118 kW (160 PS) bis 176 kW (240 PS). Ziel der Entwicklung war es, die enorme Zugkraft der schweren Sechszylindermodelle mit modernstem Bedienkomfort und einer höheren Transportgeschwindigkeit zu kombinieren. Technisch basierte die Serie auf wassergekühlten 6-Zylinder-Turbomotoren von MAN mit 6,9 Litern Hubraum und Ladeluftkühlung. Als Kraftübertragung diente das ZF-Turboshift-Getriebe, das über sechs Gänge und vier Lastschaltstufen verfügte, was in Kombination mit der Kriechgruppe insgesamt 44 Vorwärts- und 44 Rückwärtsgänge ergab. Ein herausragendes Merkmal war die serienmäßige Höchstgeschwindigkeit von 50 km/h sowie die für diese Zeit revolutionäre gefederte Vorderachse, die in Verbindung mit der bewährten Turbomatik-Flüssigkeitskupplung für einen im Großtraktorensegment unerreichten Fahrkomfort sorgte. Die Kabine der 800er-Serie bot durch ihre großzügige Verglasung und die ergonomische Anordnung der Bedienelemente auf der Seitenkonsole einen modernen Arbeitsplatz. Mit der Einführung der Baureihe wurden auch Funktionen wie die elektrohydraulische Hubwerksregelung (EHR) und ein umfassendes Überwachungssystem zum Standard. Aufgrund ihrer massiven Bauweise und der hohen Zuverlässigkeit bei schwerster Bodenbearbeitung genießen die Modelle der 800er-Serie (insbesondere der 824) heute einen legendären Ruf und erzielen auf dem Gebrauchtmarkt Spitzenpreise. Ab 1996 wurde die Serie durch die ersten 900 Vario Modelle nach oben ergänzt, blieb aber aufgrund ihres bewährten Lastschaltgetriebes bis zur Produktionseinstellung im Jahr 2003 eine beliebte Wahl für Großbetriebe und Lohnunternehmen. {| class="wikitable" | '''Favorit 800''' (1993–2004) |- | valign="top" | * {{:Traktorenlexikon: Create|Favorit 816}} (FWA 816) 125 kW (170 PS) * {{:Traktorenlexikon: Create|Favorit 818}} (FWA 818) 140 kW (190 PS) * {{:Traktorenlexikon: Create|Favorit 822}} (FWA 822) 154 kW (210 PS) * {{:Traktorenlexikon: Create|Favorit 824}} (FWA 824) 169 kW (230 PS) |} ==== Favorit 900 Vario==== [[File:Fendt Favorit 926 Vario Agritechnica 2025 (DSC04511).jpg|thumb|Fendt Favorit 926 Vario (eckige Haube)]] [[File:Eppingen-Adelshofen - Fendt Favorit 926 Vario - 23.06.2019 15-08-46.jpg|thumb|Fendt Favorit 926 Vario (runde Haube)]] Die Fendt Favorit 900 Vario Serie markierte bei ihrer Vorstellung im Jahr 1996 eine Zäsur in der Landtechnikgeschichte, da sie als weltweit erste Großtraktorenbaureihe mit einem stufenlosen Getriebe ausgestattet war. Mit dem Flaggschiff-Modell 926 Vario setzte Fendt einen neuen Industriestandard für schwere Schlepper, die maximale Zugkraft mit präziser Geschwindigkeitskontrolle kombinierten. Die Baureihe umfasste in der ersten Generation Leistungen von 118 kW (160 PS) bis 191 kW (260 PS) und löste die Position der Serie 800 als technologische Speerspitze ab. Das Herzstück der Serie war das ML-200-Vario-Getriebe, eine leistungsverzweigte Einheit, die es ermöglichte, vollkommen ohne Schaltstufen von 0,02 km/h bis zu einer Höchstgeschwindigkeit von 50 km/h zu fahren. Als Antrieb dienten wassergekühlte 6-Zylinder-Turbomotoren von MAN mit 6,9 Litern Hubraum, die für ihr hohes Drehmoment und ihre Effizienz bekannt waren. Ein wesentliches Merkmal war die Integration des Varioterminals, das erstmals eine zentrale, digitale Steuerung aller Maschinenfunktionen sowie ein automatisiertes Vorgewendemanagement ermöglichte. Zusätzlich zum innovativen Antrieb bot die 900er Serie einen überragenden Fahrkomfort durch die serienmäßige Vorderachsfederung und eine pneumatische Kabinenfederung, die Vibrationen bei schweren Ackerarbeiten und schnellen Straßenfahrten effektiv minimierten. Das Design war geprägt von einer bulligen Erscheinung und der charakteristischen, geräumigen Kabine, die eine exzellente Rundumsicht bot. Durch den Erfolg des stufenlosen Konzepts wurde der Name „Vario“ zum Markenzeichen von Fendt. In den frühen 2000er Jahren entfiel der Namenszusatz „Favorit“, und die Serie wurde als Fendt 900 Vario kontinuierlich weiterentwickelt (bis hin zu Modellen mit über 400 PS), wobei sie bis heute die technologische Referenz im Bereich der Standard-Großtraktoren darstellt. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|Favorit 916 Vario}} 132 kW (180 PS) * {{:Traktorenlexikon: Create|Favorit 920 Vario}} 162 kW (220 PS) * {{:Traktorenlexikon: Create|Favorit 924 Vario}} 176 kW (240 PS) * {{:Traktorenlexikon: Create|Favorit 926 Vario}} 191 kW (260 PS) |} === Xylon === [[File:Xylon 520.JPG|thumb|Fendt Xylon 520]] Der Fendt Xylon wurde 1994 als innovatives Systemfahrzeug eingeführt und stellte eine radikale Weiterentwicklung des klassischen Geräteträger-Konzepts dar. Er wurde entwickelt, um als universelle Arbeitsmaschine in der Landwirtschaft, im Kommunaldienst und in der Forstwirtschaft zu fungieren. Im Gegensatz zum herkömmlichen Standardtraktor zeichnete sich der Xylon durch seine Bauweise mit vier Anbauräumen (Front, Heck, Aufbauraum über der Hinterachse und Frontladeranbau) sowie eine mittig angeordnete, großzügige Kabine aus, die eine hervorragende Rundumsicht auf alle Arbeitsbereiche bot. Technisch basierte der Xylon auf der bewährten Antriebstechnik der Favorit 500 Serie. Er war mit wassergekühlten 4-Zylinder-Turbomotoren von MAN ausgestattet, die Leistungen von 88 kW (120 PS) beim Modell 520 bis zu 108 kW (147 PS) beim Modell 524 erbrachten. Als Getriebe diente das ZF-Turboshift-Getriebe mit 44 Vorwärts- und 44 Rückwärtsgängen inklusive der Turbomatik-Flüssigkeitskupplung, was dem Fahrzeug eine Höchstgeschwindigkeit von 50 km/h ermöglichte. Ein besonderes technisches Merkmal war die gefederte Vorderachse mit Niveauregulierung, die zusammen mit der zentralen Kabinenposition für einen überdurchschnittlichen Fahrkomfort und eine optimale Gewichtsverteilung sorgte. Trotz seiner enormen Vielseitigkeit und der Fähigkeit, komplexe Gerätekombinationen in einem Arbeitsgang zu führen, blieb der Xylon ein Nischenfahrzeug. Die aufwendige Konstruktion und die damit verbundenen hohen Anschaffungskosten sowie der Trend zu noch leistungsstärkeren Standardtraktoren mit stufenlosem Getriebe führten dazu, dass die Produktion im Jahr 2004 eingestellt wurde. Dennoch genießt der Xylon heute aufgrund seines einzigartigen Konzepts und seiner Robustheit einen besonderen Status und ist insbesondere im kommunalen Winterdienst und bei spezialisierten Lohnunternehmen weiterhin im Einsatz. {| class="wikitable" | '''Fendt Xylon''' |- | valign="top" | * {{:Traktorenlexikon: Create|Xylon 320}} 88 kW (120 PS) * {{:Traktorenlexikon: Create|Xylon 520}} 81 kW (110 PS) * {{:Traktorenlexikon: Create|Xylon 522}} 92 kW (125 PS) * {{:Traktorenlexikon: Create|Xylon 524}} 108 kW (147 PS) |} === Vario === Die Fendt Vario Modelle repräsentieren seit ihrer Markteinführung im Jahr 1995 die technologische Speerspitze der Marke und markieren den vollständigen Übergang zum stufenlosen Antrieb in allen Leistungsklassen. Das Herzstück dieser Traktoren ist das Vario-Getriebe, ein leistungsverzweigtes, hydrostatisch-mechanisches Getriebe, das es ermöglicht, jede Geschwindigkeit von 20 Metern pro Stunde bis zu 60 km/h ohne Schaltstufen und ohne Zugkraftunterbrechung zu erreichen. Dieses System revolutionierte die Branche, da es die Motorleistung stets im optimalen Bereich hielt und den Bedienkomfort durch die Steuerung via Fahrhebel oder Fahrpedal radikal vereinfachte. Technisch zeichnen sich die Vario-Modelle durch eine tiefgreifende Integration von Elektronik und Mechanik aus, die im Varioterminal zusammenläuft. Dieses zentrale Steuerungssystem ermöglicht die präzise Kontrolle über das Getriebemanagement (TMS), die Hydraulik, die Zapfwellen und moderne Spurführungssysteme. Über die Jahrzehnte wurde das Vario-Konzept auf das gesamte Portfolio ausgeweitet – vom kompakten 200 Vario für Sonderkulturen über die Allrounder der 300er, 500er und 700er Serien bis hin zu den Großtraktoren der 900er und 1000er Vario Serien mit bis zu 517 PS. Die Vario-Technologie hat Fendt weltweit als Innovationsführer etabliert und sorgt durch die hohe Effizienz und den enormen Wiederverkaufswert für eine Sonderstellung auf dem Landmaschinenmarkt. Bei allen Modellen wird TMS nicht in der Bezeichnung aufgeführt. ==== 200 Vario V/F/P ==== [[File:Fendt 211 V Vario Agritechnica 2025 (DSC04457).jpg|thumb|Fendt 211 V Vario]] Die Fendt 200 V/F/P Vario Serie stellt die spezialisierte Antwort auf die extremen Anforderungen im Wein-, Obst- und Hopfenbau dar und brachte 2009 als erste Spezialtraktoren-Baureihe weltweit das stufenlose Getriebe in dieses Segment. Die drei Buchstaben definieren dabei die Einsatzschwerpunkte: V (Vineyard) steht für den klassischen, schmalen Weinbauschlepper (ab 1,07 m Außenbreite), F für die etwas breitere Ausführung mit komfortablerer Kabine (ab 1,32 m) und P (Plantage) für den Spezialtraktor mit breiteren Achsen und höheren Hubkräften (ab 1,59 m) für den Obst- oder Hopfenanbau. Das technische Kernstück ist das für diese Größe optimierte Vario-Getriebe ML 70, das ein völlig ruckfreies Arbeiten und exaktes Manövrieren von 0,02 bis 40 km/h ermöglicht – ein entscheidender Vorteil bei Pflegearbeiten in engen Sonderkulturen. Als Antrieb dienen kompakte 3-Zylinder-Motoren von AGCO Power mit 3,3 Litern Hubraum, die in der aktuellen Generation (Gen3) durch das Mehrleistungskonzept DynamicPerformance beim Spitzenmodell 211 Vario bis zu 91 kW (124 PS) freisetzen. In der neuesten Ausbaustufe wurde die Baureihe durch das FendtONE-Bedienkonzept grundlegend digitalisiert. Die Kabine ist nun als 4-Pfosten-Einheit konzipiert, verzichtet auf einen störenden Getriebetunnel und bietet eine optionale Kat.-4-Filteranlage zum Schutz des Fahrers bei Pflanzenschutzarbeiten. Mit Features wie der gefederten Vorderachse inklusive Wankabstützung und einer leistungsstarken Load-Sensing-Hydraulik (bis zu 119 l/min) kombinieren diese kompakten Maschinen die High-Tech-Standards der Großtraktoren mit der nötigen Wendigkeit für spezialisierte Kulturen. Durch die drei Ausstattungslinien Power, Profi und Profi+ lässt sich die Serie zudem präzise auf die Bedürfnisse von Sonderkulturbetrieben oder kommunalen Einsätzen zuschneiden. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|207 V/F}} 51 kW (70 PS) * {{:Traktorenlexikon: Create|208 V/F}} 59 kW (80 PS) * {{:Traktorenlexikon: Create|209 V/F/P}} 67 kW (90 PS) * {{:Traktorenlexikon: Create|210 V/F/P}} 73 kW (100 PS) * {{:Traktorenlexikon: Create|211 V/F/P}} 81 kW (110 PS) |} ==== 200 Vario ==== [[File:Fendt 211 Vario Agritechnica 2025 (DSC04422).jpg|thumb|Fendt 211 Vario]] Die Fendt 200 Vario Serie (Standardausführung) wurde 2009 eingeführt und brachte das stufenlose Getriebe erstmals in das Segment der kompakten Standardschlepper. Sie löste die klassische Farmer 200 Serie ab und deckt den Leistungsbereich von etwa 57 kW (77 PS) bis 82 kW (111 PS) ab. Während die V/F/P-Varianten für Sonderkulturen optimiert sind, ist der Standard-200er als wendiger Allrounder für Grünlandbetriebe, die Hofwirtschaft und als Pflegeschlepper konzipiert. Technisch basiert die Serie auf einem kompakten 3-Zylinder-Motor von AGCO Power, der durch seine Spritzigkeit und die platzsparende Bauweise überzeugt. Das Vario-Getriebe ML 70 ermöglicht ein stufenloses Fahren von 20 Metern pro Stunde bis zu einer Höchstgeschwindigkeit von 40 km/h bei abgesenkter Motordrehzahl. Ein wesentlicher Vorteil gegenüber der vorangegangenen Generation ist der ebene Kabinenboden, der durch den Wegfall des Getriebetunnels realisiert wurde und in dieser Größenklasse eine herausragende Ergonomie und Bewegungsfreiheit bietet. In der aktuellen Generation wurde auch dieser Baureihe das FendtONE-Bedienkonzept spendiert, welches ein digitales Dashboard und eine neue Seitenkonsole mit Multifunktions-Joystick umfasst. Mit Ausstattungsmerkmalen wie der Vorderachsfederung, einer leistungsstarken Hydraulik und der optionalen Rückfahreinrichtung vereint der 200 Vario die Technologie der Großtraktoren mit der Kompaktheit eines mittleren Schleppers. Aufgrund seines geringen Eigengewichts und der hohen Wendigkeit ist er besonders bei Betrieben in Bergregionen sowie für Frontladerarbeiten hochgradig geschätzt. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|207 Vario}} 51 kW (70 PS) * {{:Traktorenlexikon: Create|208 Vario}} 59 kW (80 PS) * {{:Traktorenlexikon: Create|209 Vario}} 67 kW (90 PS) * {{:Traktorenlexikon: Create|210 Vario}} 73 kW (100 PS) * {{:Traktorenlexikon: Create|211 Vario}} 81 kW (110 PS) |} ==== 300 Vario ==== [[Datei:Fendt 312 Vario TMS Traktor.jpg|mini|Fendt 312 Vario TMS]] [[File:Fendt 313 Vario en Lövsta 02.jpg|thumb|Fendt 313 Vario]] Die Fendt 300 Vario Serie markierte im Jahr 2006 einen historischen Wendepunkt, indem sie die über Jahrzehnte erfolgreiche Farmer-300-Baureihe endgültig in das Zeitalter des stufenlosen Antriebs überführte. Als kompakter Standardschlepper deckt er heute ein Leistungsspektrum von etwa 100 bis 152 PS ab und gilt als eines der meistverkauften Modelle im Fendt-Portfolio. Sein Erfolg basiert auf der Kombination aus einem geringen Eigengewicht von rund 5 Tonnen und einer hohen Nutzlast, was ihn zum idealen Allrounder für Grünlandbetriebe, den Ackerbau und professionelle Frontladerarbeiten macht. Technisch wird die Serie in der aktuellen Generation von einem 4,4-Liter-AGCO-Power-Motor angetrieben, der durch das innovative Mehrleistungskonzept DynamicPerformance (DP) bedarfsabhängig zusätzliche Kraft freisetzt, sobald Nebenverbraucher wie die Klimaanlage oder die Zapfwelle Energie beanspruchen. Das bewährte Vario-Getriebe ML 75 sorgt für eine stufenlose Beschleunigung von 20 Metern pro Stunde bis zur Höchstgeschwindigkeit von 40 km/h bei reduzierter Motordrehzahl, was Kraftstoff spart und das Geräuschniveau in der Kabine senkt. Mit der Einführung der vierten Generation (Gen4) hielt zudem das FendtONE-Bedienkonzept Einzug, welches die Steuerung im Traktor über ein digitales Dashboard und eine moderne Multifunktionsarmlehne mit dem Büroarbeitsplatz vernetzt. Die Baureihe ist modular in den Ausstattungslinien Power, Profi und Profi+ aufgebaut, wodurch sie sich präzise an unterschiedliche Anforderungen anpassen lässt – von der einfachen Basismaschine für den Hofbetrieb bis hin zur High-Tech-Variante mit Spurführung und ISOBUS-Steuerung für den Präzisionsackerbau. Ein besonderes Merkmal ist die hohe Bodenfreiheit von bis zu 500 mm und die integrierte Vorderachsfederung, die zusammen mit der VisioPlus-Kabine für eine überlegene Sicht und hohen Fahrkomfort sorgt. Durch ihre enorme Wertstabilität und Vielseitigkeit setzt die 300er-Vario-Serie die Tradition des „Farmer“ als universelles Arbeitspferd der modernen Landwirtschaft fort. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|309 Vario}} 70 kW (95 PS) * {{:Traktorenlexikon: Create|310 Vario}} 77 kW (105 PS) * {{:Traktorenlexikon: Create|311 Vario}} 84 kW (115 PS) * {{:Traktorenlexikon: Create|312 Vario}} 92 kW (125 PS) * {{:Traktorenlexikon: Create|313 Vario}} 99 kW (135 PS) * {{:Traktorenlexikon: Create|314 Vario}} 104KW (142 PS) |} ==== 400 Vario ==== [[File:Fendt 415 Vario, Claas Liner.jpg|thumb|Fendt 415 Vario]] Die Fendt 400 Vario Serie markierte bei ihrer Einführung im Jahr 1999 einen historischen Wendepunkt in der Modellpolitik von Fendt, da sie die weltweit erste Baureihe war, die das stufenlose Getriebe in das Segment der kompakten Vierzylinder-Traktoren brachte. Sie bildete die Brücke zwischen der kompakten 300er-Klasse und der größeren 700er-Serie und deckte ein Leistungsspektrum von etwa 95 PS bis 125 PS ab. Mit ihrem kurzen Radstand und der hohen Leistungsdichte war sie primär als wendiger Allrounder für Frontladerarbeiten, den Grünlandeinsatz und schwere Pflegearbeiten konzipiert. Ein entscheidender Einschnitt erfolgte mit der umfassenden Modellpflege im Jahr 2006: In diesem Zuge wurde die traditionsreiche Bezeichnung „Farmer“ offiziell gestrichen. Während die erste Generation noch als „Farmer 400 Vario“ vermarktet wurde, firmierte die überarbeitete Baureihe fortan schlicht als Fendt 400 Vario. Dieser Namenswechsel symbolisierte den endgültigen Übergang von der klassischen Mechanik-Ära hin zur reinen High-Tech-Marke, bei der das stufenlose Vario-Getriebe nun zum alleinigen Standard geworden war. Technisch basierte die Serie auf spritzigen 4-Zylinder-Motoren von Deutz, die in Kombination mit dem Vario-Getriebe ML 75 ein völlig ruckfreies Arbeiten von 0,02 bis 50 km/h ermöglichten. Trotz des Wegfalls des Namenszusatzes blieb der 400er ein würdiger Nachfolger der Farmer-Tradition, bot jedoch High-Tech-Komponenten der Oberklasse, wie das Varioterminal und die elektronische Hubwerksregelung (EHR). Die Leistung der Topmodelle, wie dem 415 Vario, stieg in der Ära ohne den „Farmer“-Schriftzug auf bis zu 155 PS an. Heute gilt diese Baureihe als einer der wertstabilsten Klassiker auf dem Gebrauchtmarkt, da sie die Kompaktheit der alten Farmer-Serie mit der modernen Leistungsfähigkeit der Vario-Technik vereint. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|411 Vario}} 85 kW (115 PS) * {{:Traktorenlexikon: Create|412 Vario}} 92 kW (125 PS) * {{:Traktorenlexikon: Create|413 Vario}} 99 kW (135 PS) * {{:Traktorenlexikon: Create|414 Vario}} 107 kW (145 PS) * {{:Traktorenlexikon: Create|415 Vario}} 114 kW (155 PS) |} ==== 500 Vario ==== [[File:Fendt 516 Vario Profi+ Agritechnica 2025 (DSC04534).jpg|thumb|Fendt 516 Vario]] Die Fendt 500 Vario Serie wurde im Jahr 2012 als direkter Nachfolger der erfolgreichen 400er-Baureihe eingeführt und vollendete den strategischen Verzicht auf den Namen „Farmer“. Während die Vorgängermodelle noch die historische Brücke zur Mittelklasse schlugen, wurde die 500er-Serie konsequent als kompakter Hochleistungsschlepper positioniert, der die Technik der Großtraktoren in eine handlichere Bauform transferierte. Sie deckt ein Leistungsspektrum von etwa 125 PS bis 165 PS ab und besetzt damit die wichtige Lücke zwischen dem 300 Vario und dem 6-Zylinder-Modell 700 Vario. Technisch markierte der 500 Vario einen deutlichen Sprung, da er erstmals die VisioPlus-Kabine der größeren Baureihen in diese Klasse brachte. Mit der tief heruntergezogenen Frontscheibe und dem modernen Bedienkonzept bot er eine Ergonomie, die weit über das Maß früherer Kompaktschlepper hinausging. Angetrieben wird die Serie von einem 4,04-Liter-Vierzylindermotor von Deutz, der in Kombination mit dem stufenlosen Vario-Getriebe ML 90 eine Höchstgeschwindigkeit von 50 km/h bei reduzierter Motordrehzahl ermöglicht. Diese Kombination macht den 500er zu einer extrem vielseitigen Maschine, die sowohl im schweren Frontladereinsatz als auch bei Transportarbeiten und im Ackerbau überzeugt. Mit der Einführung der neuesten Generation wurde auch hier das FendtONE-System implementiert, welches die Vernetzung zwischen der Maschine und dem Büroarbeitsplatz ins Zentrum rückt. Trotz des Fehlens des Namens „Farmer“ führt der 500 Vario dessen Erbe als universelles Arbeitspferd fort, jedoch auf einem technologischen Niveau, das Funktionen wie vollautomatisches Lenken, ISOBUS-Gerätesteuerung und präzise Spurführung zum Standard macht. Durch sein günstiges Leistungsgewicht und die kompakten Abmessungen bleibt er die erste Wahl für Betriebe, die maximale Schlagkraft bei hoher Wendigkeit fordern. {| class="wikitable" |- | valign="top" | * {{:Traktorenlexikon: Create|512 Vario}} 92 kW (125 PS) * {{:Traktorenlexikon: Create|513 Vario}} 99 kW (135 PS) * {{:Traktorenlexikon: Create|514 Vario}} 107 kW (145 PS) * {{:Traktorenlexikon: Create|516 Vario}} 121 kW (165 PS) |} {{clear}} ==== 600 Vario ==== [[File:Fendt 620 Vario Profi+ Agritechnica 2025 (DSC06624).jpg|thumb|Fendt 620 Vario]] Die Fendt 600 Vario Serie wurde im Jahr 2023 als komplett neu entwickelte Baureihe vorgestellt und besetzt das Segment der leistungsstarken Vierzylinder-Traktoren im Bereich von 150 bis 224 PS. Mit dieser Serie belebte Fendt die legendäre „600er“-Nummerierung wieder, die historisch für die schweren Favorit-Modelle stand, verzichtete jedoch konsequent auf jegliche Zusatzbezeichnungen wie „Farmer“ oder „Favorit“. Die Serie schließt die Lücke zwischen dem kompakten 500 Vario und dem großen 6-Zylinder-Modell 700 Vario und richtet sich an Betriebe, die maximale Leistung bei hoher Wendigkeit und geringem Bodendruck suchen. Das technische Herzstück ist der neu entwickelte AGCO Power Core50 Motor mit 5 Litern Hubraum, der speziell für das Niedrigdrehzahlkonzept Fendt iD konstruiert wurde. Damit erreicht der Traktor sein maximales Drehmoment bereits bei 1.350 U/min, was ihn besonders kraftstoffeffizient macht. Eine Besonderheit in dieser Leistungsklasse ist das Spitzenmodell 620 Vario, das durch das Mehrleistungskonzept DynamicPerformance (DP) bis zu 224 PS freisetzt. Kraftübertragung und Antrieb werden über den neuen VarioDrive-Fahrantrieb geregelt, der die Vorder- und Hinterachse unabhängig voneinander ansteuert, wodurch der „Pull-in-turn“-Effekt für einen extrem kleinen Wendekreis sorgt. Die 600er-Serie übernimmt die großräumige VisioPlus-Kabine und ist vollständig in das digitale Ökosystem FendtONE integriert. Dies ermöglicht eine lückenlose Vernetzung von der Maschine bis in das Büro und unterstützt Funktionen für den Präzisionsackerbau. Trotz des Verzichts auf die historischen Namen zeigt die Serie durch ihre Robustheit und das universelle Einsatzspektrum – vom schweren Zugbetrieb über Transportaufgaben bis hin zu Zapfwellenarbeiten –, dass sie die Tradition der vielseitigen Allrounder in die moderne, digitale Landwirtschaft überführt. Mit einem zulässigen Gesamtgewicht von bis zu 13,5 Tonnen bietet der 600 Vario zudem eine Nutzlast, die ihn für professionelle Lohnunternehmen und Großbetriebe prädestiniert. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|614 Vario}} 110 kW (149 PS) * {{:Traktorenlexikon: Create|616 Vario}} 124 kW (169 PS) * {{:Traktorenlexikon: Create|618 Vario}} 139 kW (189 PS) * {{:Traktorenlexikon: Create|620 Vario}} 154 kW (209 PS) |} ==== 700 Vario ==== [[File:Fendt 728 Vario Gen7 Agritechnica 2023 (DSC05099).jpg|thumb|Fendt 728 Vario Gen7]] Die Fendt 700 Vario Serie wurde im Jahr 1998 eingeführt und markierte den endgültigen Verzicht auf die traditionsreichen Namen „Farmer“ und „Favorit“ im mittleren bis oberen Leistungssegment. Als Nachfolger der Favorit 500er-Serie konzipiert, wurde sie schnell zum Herzstück des Fendt-Programms und gilt heute als der meistverkaufte Universaltraktor in Deutschland. Mit der rein numerischen Bezeichnung signalisierte Fendt den Aufbruch in eine neue Ära, in der die Vario-Technik zum Standard für professionelle Allround-Maschinen wurde. Technisch setzte der 700 Vario Maßstäbe durch die Kombination aus kompakten Abmessungen und leistungsstarken 6-Zylinder-Motoren von Deutz. Das Vario-Getriebe ML 140 (beziehungsweise ML 180 bei den größeren Modellen) ermöglichte ein stufenloses Fahren bis zu 50 km/h und später sogar 60 km/h. Die Serie deckte ursprünglich einen Bereich von 115 bis 160 PS ab, hat sich jedoch über sieben Generationen hinweg kontinuierlich weiterentwickelt. In der aktuellen Generation Gen7 erreicht das Spitzenmodell 728 Vario dank des AGCO Power Core75 Motors und des Mehrleistungskonzepts DynamicPerformance (DP) eine Maximalleistung von bis zu 303 PS. Ein wesentliches Merkmal des 700 Vario ist seine enorme Vielseitigkeit: Er ist schwer genug für die Bodenbearbeitung, aber gleichzeitig wendig und leicht genug für den Frontladereinsatz und die Grünlandarbeit. Mit der Einführung der VisioPlus-Kabine und dem digitalen Bedienkonzept FendtONE wurde die Serie zudem zum Vorreiter in Sachen Smart Farming und Ergonomie. Trotz des Fehlens der Bezeichnung „Favorit“ führt der 700 Vario dessen Erbe als technologisch führender Mehrzweckschlepper fort und ist heute weltweit der Standard für mittlere bis große landwirtschaftliche Betriebe sowie Lohnunternehmen. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|712 Vario}} 96 kW (130 PS) * {{:Traktorenlexikon: Create|714 Vario}} 107 kW (145 PS) * {{:Traktorenlexikon: Create|716 Vario}} 121 kW (165 PS) * {{:Traktorenlexikon: Create|718 Vario}} 132 kW (180 PS) * {{:Traktorenlexikon: Create|720 Vario}} 147 kW (200 PS) * {{:Traktorenlexikon: Create|722 Vario}} 162 kW (220 PS) * {{:Traktorenlexikon: Create|724 Vario}} 176 kW (240 PS) |- | ab 2022 (Gen.7 mit AGCO Motoren) |- | valign="top" | * {{:Traktorenlexikon: Create|720 Vario}} 149 kW (203 PS) * {{:Traktorenlexikon: Create|722 Vario}} 164 kW (223 PS) * {{:Traktorenlexikon: Create|724 Vario}} 179 kW (243 PS) * {{:Traktorenlexikon: Create|726 Vario}} 193 kW (262 PS) * {{:Traktorenlexikon: Create|728 Vario}} 208 kW (283 PS) |} Die Links gehen noch auf die "alten" 700er. ==== 800 Vario ==== [[Datei:Fendt 820 Vario TMS Traktor mit Einachskippanhänger (2024).jpg|mini|Fendt 820 Vario TMS]] [[File:Fendt 832 Vario Gen5 Profi+ Agritechnica 2025 (DSC04437).jpg|thumb|Fendt 832 Vario Gen5]] Die Fendt 800 Vario Serie markierte im Jahr 2003 eine Zäsur in der Modellstrategie, da mit ihrer Einführung die traditionsreiche Bezeichnung „Favorit“ endgültig abgelegt wurde. Fortan firmierte die Baureihe als rein numerische Serie, was den strategischen Fokus vollständig auf das technologische Alleinstellungsmerkmal, das stufenlose Vario-Getriebe, rückte. Dieser Namenswechsel signalisierte den Aufbruch in eine Ära, in der High-Tech-Komponenten nicht mehr als Zusatzoption, sondern als integraler Standard der Marke definiert wurden. Technisch präsentierte sich die Serie ab 2003 zunächst mit den Modellen 815, 817 und 818 Vario TMS, die eine Brücke zwischen der kompakten Mittelklasse und den schweren Großtraktoren schlugen. Ausgestattet mit spritzigen 6-Zylinder-Motoren von Deutz und dem Vario-Getriebe ML 160, boten diese Maschinen Leistungen von bis zu 190 PS in einer für diese Kraftklasse ungewöhnlich kompakten Bauform. Ein zentrales Merkmal war das serienmäßige Tractor Management System (TMS), das die elektronische Vernetzung von Motor und Getriebe übernahm, um stets im wirtschaftlichen Optimum zu arbeiten und den Fahrer massiv zu entlasten. Über die Jahre entwickelte sich die 800 Vario Serie zu einem Synonym für hocheffiziente Transport- und Ackerleistung. In späteren Ausbaustufen, wie der 800 Vario SCR ab 2010, stieg die Leistung der Topmodelle auf bis zu 287 PS an. Trotz dieser enormen Kraftentfaltung blieb die Serie ihrem Ruf als wendiger Großtraktor treu, der durch Features wie die 60 km/h Höchstgeschwindigkeit, die leistungsstarke Hydraulik und das moderne Varioterminal bestach. Heute gelten insbesondere die frühen Modelle ab 2003 aufgrund ihrer Robustheit und der wegweisenden Bedienlogik als gesuchte Klassiker, die den Grundstein für das heutige, rein numerische Produktsortiment von Fendt legten. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|815 Vario}} 110 kW (150 PS) * {{:Traktorenlexikon: Create|817 Vario}} 121 kW (165 PS) * {{:Traktorenlexikon: Create|818 Vario}} 136 kW (185 PS) * {{:Traktorenlexikon: Create|820 Vario}} 152 kW (205 PS) {{:Traktorenlexikon: Create|820 Vario greentec}} 151 kW (205 PS) | valign="top" | {{:Traktorenlexikon: Create|819 Vario}} 147 kW (200 PS) * {{:Traktorenlexikon: Create|822 Vario}} 162 kW (220 PS) * {{:Traktorenlexikon: Create|824 Vario}} 176 kW (240 PS) * {{:Traktorenlexikon: Create|826 Vario}} 191 kW (260 PS) * {{:Traktorenlexikon: Create|828 Vario}} 206 kW (280 PS) | valign="top" | {{:Traktorenlexikon: Create|826 Vario}} 208 kW (283 PS) * {{:Traktorenlexikon: Create|829 Vario}} 230 kW (313 PS) * {{:Traktorenlexikon: Create|832 Vario}} 252 kW (343 PS) |} ==== 900 Vario ==== [[Datei:Fendt 936.jpg|thumb|[[Traktorenlexikon: Fendt 936 Vario|Fendt 936 Vario „Black Beauty“]]]] [[File:Dowerin Field Days, 2021 (50).jpg|thumb|Fendt 939 Vario]] Die Fendt 900 Vario Serie markierte im Jahr 2005 einen entscheidenden Wendepunkt in der Geschichte der Großtraktoren, als mit der Einführung der vierten Generation (Modelle 922 bis 936 Vario). Der Fendt 936 Vario wird als „Black Beauty“ auf der AGRITECHNICA 2005 zum absoluten Hingucker. Während der erste Vario der Welt im Jahr 1995 noch als „Favorit 926 Vario“ firmierte. Ab der dritten Generation (dem Fendt Vario 930 TMS aus dem Jahr 2002) Dieser Verzicht auf den Namen „Favorit“ unterstrich den Anspruch, das Vario-Getriebe nicht mehr als technologische Besonderheit, sondern als das fundamentale Markenzeichen aller Fendt-Großschlepper zu etablieren. Technisch setzte die 900er-Serie ab 2005 neue Maßstäbe im schweren Ackerbau und im schnellen Transport. Ausgestattet mit 6-Zylinder-Deutz-Motoren und einem Hubraum von 7,1 Litern, deckten die Maschinen einen Leistungsbereich von 220 PS bis hin zum damaligen Spitzenmodell 936 Vario mit 360 PS ab. Das Herzstück war das Vario-Getriebe ML 260, das eine Höchstgeschwindigkeit von 60 km/h ermöglichte. Ein technologischer Meilenstein war die Einführung der unabhängigen Vorderachsfederung mit Niveauregulierung sowie die Integration des Varioterminals, das die Kontrolle über Getriebe, Hydraulik und ISOBUS-Geräte zentralisierte. In den darauffolgenden Generationen wurde die Serie konsequent weiterentwickelt und erreichte mit der aktuellen Baureihe (Gen7) Leistungen von bis zu 415 PS. Trotz des Fehlens der Bezeichnung „Favorit“ blieb die 900er-Serie das globale Flaggschiff für den schweren Zugbetrieb, zeichnete sich jedoch durch das innovative Niedrigdrehzahlkonzept Fendt iD und das neue VarioDrive-Antriebssystem aus, das die Vorder- und Hinterachse unabhängig voneinander ansteuert. Heute gilt der 900 Vario als einer der erfolgreichsten Großtraktoren weltweit, der die Robustheit der alten Favorit-Modelle mit modernster digitaler Vernetzung (FendtONE) und maximaler Kraftstoffeffizienz kombiniert. {| class="wikitable" | valign="top" | {{:Traktorenlexikon: Create|916 Vario}} 146 kW (198 PS) {{:Traktorenlexikon: Create|920 Vario}} 154 kW (210 PS) {{:Traktorenlexikon: Create|922 Vario}} 162 kW (220 PS) {{:Traktorenlexikon: Create|924 Vario}} 176 kW (240 PS) {{:Traktorenlexikon: Create|926 Vario}} 199 kW (270 PS) | valign="top" | {{:Traktorenlexikon: Create|927 Vario}} 199 kW (270 PS) {{:Traktorenlexikon: Create|930 Vario}} 220 kW (300 PS) {{:Traktorenlexikon: Create|933 Vario}} 242 kW (330 PS) {{:Traktorenlexikon: Create|936 Vario}} 265 kW (360 PS) {{:Traktorenlexikon: Create|939 Vario}} 287 kW (390 PS) | valign="top" | {{:Traktorenlexikon: Create|942 Vario}} 305 kW (415 PS) |} ==== 1000 Vario ==== [[File:Fendt 1052 Vario Gen4 Profi+ Agritechnica 2025 (DSC01013).jpg|thumb|Fendt 1052 Vario Gen4]] Die Fendt 1000 Vario Serie wurde im Jahr 2014 als vollkommen neue Leistungsklasse vorgestellt und 2015 im Markt eingeführt. Anders als die kleineren Baureihen besaß diese Serie von Beginn an keine historische Zusatzbezeichnung wie „Farmer“ oder „Favorit“, sondern wurde direkt in das rein numerische System eingeordnet. Sie begründete das Segment der „Großstandardtraktoren“: Maschinen, welche die enorme Zugkraft von Knicklenkern oder Raupenschleppern bieten, aber gleichzeitig die Wendigkeit und Straßentauglichkeit eines Standardtraktors beibehalten. Technisch stellt der 1000er Vario das Flaggschiff der Marke dar und deckt mit vier Modellen (1038 bis 1050) einen Leistungsbereich von 396 bis 517 PS ab. Als Kraftquelle dient ein 12,4-Liter-Sechszylindermotor von MAN, der speziell auf das Niedrigdrehzahlkonzept Fendt iD optimiert ist. Das maximale Drehmoment von bis zu 2.400 Nm liegt bereits bei 1.100 U/min an, was die Effizienz bei schwerster Bodenbearbeitung massiv steigert. Eine wesentliche Innovation ist das VarioDrive-Antriebssystem, bei dem die Vorder- und Hinterachse über zwei getrennte Hydromotoren unabhängig voneinander angetrieben werden. Dies macht eine starre Allradkupplung überflüssig und reduziert den Wendekreis durch den „Pull-in-turn“-Effekt erheblich. Trotz seiner Größe und einem Leergewicht von rund 14 Tonnen ist der 1000 Vario für eine Höchstgeschwindigkeit von bis zu 60 km/h zugelassen und bleibt durch seine Bauweise voll straßentauglich. In der aktuellen Ausführung ist er vollständig in das FendtONE-Ökosystem integriert, was eine präzise Steuerung komplexer Arbeitsabläufe und eine lückenlose Dokumentation ermöglicht. Der 1000 Vario hat Fendt weltweit in einem Leistungssegment etabliert, das zuvor fast ausschließlich von Spezialmaschinen dominiert wurde, und gilt heute als technologische Referenz für modernste Großtraktoren-Technik. {| class="wikitable" ! ab 2015 |- | valign="top" | {{:Traktorenlexikon: Create|1038 Vario}} (380 PS) {{:Traktorenlexikon: Create|1042 Vario}} (420 PS) {{:Traktorenlexikon: Create|1046 Vario}} (460 PS) {{:Traktorenlexikon: Create|1050 Vario (FWA 530.23)}} (517 PS) |} === Raupentraktoren === [[File:Fendt 1100 MT-2-crop.jpg|thumb|Fendt 1100 Vario MT]] Mit der Integration der Raupentechnologie in das Produktportfolio hat Fendt das Segment der schweren Bodenschonung und maximalen Zugkraft grundlegend transformiert. Die Basis dieser Entwicklung bildet die jahrzehntelange Erfahrung der Marke Challenger, die einst Pionierarbeit bei Gummilaufwerken leistete. Unter der Federführung von Fendt wurden diese Maschinen jedoch technisch völlig neu definiert: Der harte mechanische Antrieb der ursprünglichen Raupenschlepper wurde durch das stufenlose Vario-Getriebe ersetzt, während das moderne Bedienkonzept die Handhabung auf das Niveau der Radtraktoren hob. Heute stehen die Vario MT Modelle für eine hocheffiziente Kraftübertragung bei geringstem Bodendruck, konzipiert für professionelle Betriebe, die unter schwierigsten Bedingungen höchste Schlagkraft fordern. ==== 900 Vario MT ==== [[File:Dowerin Field Days, 2021 (47).jpg|thumb|Fendt 943 Vario MT]] Der Fendt 900 Vario MT markiert den Einstieg in die Welt der stufenlosen Raupenschlepper und deckt den Leistungsbereich von 380 bis 431 PS ab. Als technologischer Nachfolger der Challenger MT700 Serie wurde er gezielt für die Kombination aus hoher Zugleistung und modernstem Fahrkomfort entwickelt. Das Herzstück ist der 9,1-Liter-AGCO-Power-Motor, der perfekt mit dem für Raupen optimierten VarioDrive-Getriebe harmoniert. Ein herausragendes Merkmal ist das innovative Fahrwerkskonzept: Das SmartRide-System kombiniert eine Federung des Hauptrahmens mit den ConstantTouch-Laufrollen, wodurch sich das Laufwerk wie ein Panzerkettenlaufwerk flexibel an Bodenunebenheiten anpasst. Dies sorgt für eine konstante Aufstandsfläche und verhindert das typische „Nicken“ der Maschine. In Verbindung mit der großräumigen Kabine und dem digitalen Bedienkonzept bietet der 900 Vario MT einen Komfort, der in dieser Spezialklasse zuvor nicht verfügbar war. Er ist die ideale Wahl für Betriebe, die schwere Bodenbearbeitung und Saatbettbereitung mit maximaler Bodenschonung verbinden müssen. {| class="wikitable" ! ab 2018 |- | valign="top" | {{:Traktorenlexikon: Create|938 Vario MT}} {{:Traktorenlexikon: Create|940 Vario MT}} {{:Traktorenlexikon: Create|943 Vario MT}} |} ==== 1100 MT ==== [[File:Fendt 1167 Vario MT Agritechnica 2023 (DSC05922).jpg|thumb|Fendt 1167 Vario MT]] Als unangefochtenes Kraftpaket im Sortiment ist der Fendt 1100 Vario MT für die extremsten Anforderungen des Weltmarkts gebaut. In dieser Baureihe, die Leistungen von 511 bis zu 673 PS erreicht, wurde die einstige Challenger-Großraupentechnik vollständig mit der Fendt-Philosophie verschmolzen. Angetrieben von MAN-Sechszylindermotoren mit bis zu 16,2 Litern Hubraum, setzt die Maschine auf das Fendt iD Niedrigdrehzahlkonzept. Dies ermöglicht es, das enorme Drehmoment von bis zu 3.100 Nm bereits in einem sehr niedrigen Drehzahlbereich abzurufen und hocheffizient in Vortrieb umzusetzen. Die technische Überlegenheit zeigt sich besonders bei der Arbeit mit extrem breiten Anbaugeräten. Durch das stufenlose Getriebe und das schwenkbare Heckzugpendel bleibt die Maschine auch unter Volllast präzise manövrierbar, ohne dass die Ketten beim Lenken den Boden übermäßig aufreißen. Mit einem Einsatzgewicht von bis zu 31 Tonnen und der Integration in das FendtONE-System für die digitale Dokumentation und Spurführung stellt der 1100 Vario MT die Spitze der modernen Landtechnik dar. Er ist spezialisiert auf den Einsatz auf weitläufigen Flächen, wo herkömmliche Radtraktoren aufgrund ihrer physischen Grenzen bei der Zugkraftübertragung und Bodenbelastung ausscheiden. {| class="wikitable" ! ab 2018 ! ab 2022 |- | valign="top" | {{:Traktorenlexikon: Create|1149 Vario}} {{:Traktorenlexikon: Create|1154 Vario}} {{:Traktorenlexikon: Create|1159 Vario}} {{:Traktorenlexikon: Create|1165 Vario}} | valign="top" | {{:Traktorenlexikon: Create|1162 Vario}} {{:Traktorenlexikon: Create|1167 Vario}} |} === Agrobil S === [[File:Fendt Agrobil S.jpeg|thumb|Fendt Agrobil S]] Das Fendt Agrobil markiert den Einstieg von Fendt in die Welt der spezialisierten Ernte-Selbstfahrer und ist das direkte Ergebnis der Übernahme der Maschinenfabrik Dechentreiter im Jahr 1970. Dechentreiter hatte mit dem „Lely-Trac“ bereits Pionierarbeit geleistet, doch erst unter der Führung von Fendt wurde das Konzept zur Serienreife des Agrobil S weiterentwickelt. Das Fahrzeug kombinierte die Funktionen eines Ladewagens mit einem eigenständigen Fahrgestell, um die Futterernte schneller und mobiler zu gestalten. Mit seinem markanten Design – dem vorne sitzenden Fahrerhaus und dem luftgekühlten Deutz-Motor – bot es eine für die 1970er Jahre beeindruckende Transportgeschwindigkeit von bis zu 40 km/h und eine hervorragende Übersicht bei der Aufnahme des Ernteguts. Obwohl das Agrobil aufgrund seiner spezialisierten Ausrichtung und der geringen Ganzjahresauslastung ein Nischenprodukt blieb, legte die Integration der Dechentreiter-Technik den Grundstein für den Ausbau des Standorts Bäumenheim, der heute als Kompetenzzentrum für Kabinenbau eine zentrale Rolle im Konzern spielt. {{Wikipedia1|Agrobil}} {| class="wikitable" | valign="top" | * [[Traktorenlexikon: Fendt Agrobil S|Agrobil S]] 59 kW (80 PS) |} === TriSix === [[Datei:Fendt Trisix.JPG|thumb|Fendt [[Traktorenlexikon: Fendt TriSix|TriSix]]]] Der Fendt Trisix wurde im Jahr 2007 auf der Agritechnica als spektakuläre Konzeptstudie vorgestellt und markierte den Versuch, die Grenzen der Traktorenkonstruktion völlig neu zu definieren. Das auffälligste Merkmal dieser Maschine war das Fahrwerkkonzept mit drei Achsen (6x6-Antrieb), das speziell darauf ausgelegt war, enorme Motorleistung in maximale Zugkraft bei gleichzeitig hoher Bodenschonung und voller Straßentauglichkeit umzusetzen. Technisch basierte der Trisix auf Komponenten der Großtraktoren-Serie, trieb diese jedoch ins Extrem. Ausgestattet mit einem MAN-Sechszylindermotor, der eine Leistung von rund 540 PS erbrachte, nutzte das Fahrzeug zwei stufenlose Vario-Getriebe, um die Kraft auf alle sechs Räder zu verteilen. Durch die drei Achsen erreichte der Trisix eine deutlich größere Aufstandsfläche als herkömmliche Standardtraktoren, was den Bodendruck massiv reduzierte. Ein technisches Highlight war die Lenkung: Die erste und die dritte Achse waren aktiv gelenkt, was dem über 7 Meter langen Gespann eine Wendigkeit verlieh, die man sonst nur von deutlich kleineren Maschinen kannte. Trotz der beeindruckenden Leistungsdaten und des enormen öffentlichen Interesses blieb der Trisix ein reiner Technologieträger. Die Komplexität des Antriebsstrangs, das hohe Eigengewicht von rund 19 Tonnen und die damit verbundenen Produktionskosten standen einer Serienfertigung entgegen. Dennoch war das Projekt für die Entwicklung der Marke von großer Bedeutung: Viele der gewonnenen Erkenntnisse über das Niedrigdrehzahlkonzept, die Steuerung extrem hoher Drehmomente und die Optimierung der Bodenschonung flossen Jahre später direkt in die Entwicklung der 1000 Vario Serie ein. Heute gilt der Trisix als einer der bekanntesten Prototypen der Landtechnikgeschichte, der den Mut der Ingenieure verdeutlicht, radikal neue Wege in der Traktorenarchitektur zu gehen. {| class="wikitable" | valign="top" | * [[Traktorenlexikon: Fendt TriSix|TriSix]] 408 kW (540 PS) |} == Literatur == * Oliver Aust: ''Fendt – Traktoren im Einsatz.'' Podszun, 2004. ISBN 9783861333555. * Peter Glienke, Kai W. Richters: ''Fendt – Schlepper im Einsatz.'' Schwungrad-Verlag, 2006. ISBN 9783933426161. * Klaus Herrmann: ''75 Jahre Dieselross – Eine Erfolgsgeschichte.'' Ulmer, 2005. ISBN 9783800147939. * Klaus Herrmann: ''Die Fendt-Chronik – Vom Dieselross zum Vario.'' 3. Aufl., BLV, 2002. ISBN 9783405157876; 4. Aufl., Ulmer, 2006. ISBN 9783800148639. * Gilbert Kremer: ''Das Fendt Werkstattbuch – Einstelldaten für Fendt-Schlepper – Motoren von MWM und Deutz.'' Verlag Klaus Rabe, 2009. ISBN 9783926071408. * Gilbert Kremer: ''Fendt Geschichte – Das Beste aus der Fendt-Werbung von 1930 bis heute.'' DLG-Verlag, 2009. ISBN 9783769007367. * Gilbert Kremer: ''Alle Traktoren von Fendt – Typen und Daten.'' Verlag Klaus Rabe, 2005. ISBN 9783926071330. * Gilbert Kremer: ''Fendt-Geschichten – Das Beste aus den Fendt-Nachrichten.'' DLG-Verlag, 2003. ISBN 9783769006100. * Gilbert Kremer, Kurt Häfner (Hg.): ''Fendt – Schlepper-Prospekte von 1979 bis 1999.'' Kosmos, 2001. ISBN 9783440081167. * Gilbert Kremer, Kurt Häfner (Hg.): ''Fendt – Schlepper-Prospekte von 1966 bis 1978.'' Kosmos, 1998. ISBN 9783440076187. * Gilbert Kremer, Kurt Häfner (Hg.): ''Fendt – Schlepper-Prospekte von 1930 bis 1966.'' Kosmos, 1997. ISBN 9783440073223. * Albert Mößmer: ''Typenatlas Fendt Traktoren.'' Garamond, 2011. ISBN 9783862456277. * Albert Mößmer: ''Fendt – Das Typenbuch 1928 bis heute.'' Geramond, 2006. ISBN 9783765477010. * Albert Mößmer: ''Fendt Dieselroß - Typengeschichte und Technik.'' GeraMond, 2014. ISBN 9783862455553. * Wolfgang Schiffer: ''Fendt Traktoren – Ackern wie damals – Die Fendt-Legende in Bildern.'' Landwirtschaftsverlag, 2007. ISBN 9783784334516. * Peter Schneider: ''Typenkompass Fendt – Schlepper und Traktoren seit 1974.'' Bd. 2, Motorbuch Verlag, 2010. ISBN 9783613031692. * Peter Schneider: ''Typenkompass Fendt – Schlepper und Traktoren 1928–1993.'' Bd. 1, Motorbuch Verlag, 2010. ISBN 9783613031708. * Klaus Tietgens: ''Die Fendt Geräteträger Chronik.'' Podszun, 2007. ISBN 9783861334187. == Weblinks == {{Commons|Category:Fendt tractors|Fendt-Traktoren}} {{Wikipedia1|Fendt (Marke)}} * [http://www.fendt.com/ www.fendt.com] * [http://www.fendt-oldtimer.de www.fendt-oldtimer.de] Das offizielle Forum zu Fendt-Oldtimern * [http://de.tec24.com/c-10100/m-fendt http://de.tec24.com/c-10100/m-fendt] Gebrauchte Fendt-Traktoren * [https://www.technikboerse.com/gebraucht/traktor-357/fendt-370 technikboerse.com] Gebrauchte Fendt-Traktoren auf technikboerse.com * [http://www.oldtimerplus.de/blog/tag/fendt/ www.oldtimerplus.de/blog/tag/fendt/] Blog mit Infos für Fendt-Fans Tabellarische Typen, Übersicht vieler Fendt-Schlepper: www.tractorbook.de/traktoren/fendt * http://www.fendt-freunde.de/datenbank/fs_dat1.htm * http://www.fendt-freunde.de/datenbank/fs_dat2.htm == Einzelnachweise == <references /> {{:Traktorenlexikon: Navigation}} 1bu4ota75qyeg0uzfu4n4ptul1ttrq5 1092062 1092061 2026-10-10T15:46:12Z Bent07 117026 /* 1000 Vario */ Motorleistungen korrigiert 1092062 wikitext text/x-wiki {{:Traktorenlexikon: Navigation}} [[Datei:Fendt turbomatik frontlader.jpg|thumb|Fendt Farmer Turbomatik mit Frontlader]] [[Datei:FendtBadge-crop.jpg|alternativtext=|mini|Fendt Kühler-Logo]] '''Fendt''' ist ein deutscher Traktorenhersteller mit Sitz in [[w:Marktoberdorf|Marktoberdorf]] im Allgäu und seit Ende der 1990er Jahre eine Marke des US-amerikanischen Konzerns [[Traktorenlexikon: AGCO|AGCO]]. Produktionsstandorte befinden sich in Marktoberdorf (Allgäu), Asbach-Bäumenheim und Hohenmölsen. ==Geschichte== [[Datei:Fendt Marktoberdorf aerial 2005.jpg|thumb|Fendt-Werk in Marktoberdorf im Jahr 2006 (rechts im Bild)]] Als Johann Georg Fendt begann, in seiner Mechanikerwerkstatt Traktoren zu bauen, ahnte er noch wenig von den großen Veränderungen, die die Traktorentechnik in diesem Jahrhundert durchlaufen sollte. Die Richtung stand von Anfang an fest: Die Kunden durch neue technische Lösungen, Wirtschaftlichkeit und Zuverlässigkeit zu überzeugen. Am 9. Dezember 1933 starb Joh. Georg Fendt. Firmeninhaberin wurde seine Witwe Kreszentia Fendt, Betriebsleiter sein ältester Sohn Xaver, der 1934 auch die Meisterprüfung ablegte. Die Xaver Fendt & Co. wurde am 31. Dezember 1937 in das Handelsregister in Kempten mit den Gesellschaftern Kreszentia, Xaver und Hermann Fendt eingetragen. === Entwicklungsgeschichte in Jahreszahlen === *'''1927''' Es entstand zunächst ein Kleinschlepper mit einem Benzinmotor, aber bereits einem fahrunabhängigen Mähwerk. *'''1928''' Der erste europäische Dieselkleinschlepper mit 6 PS, Anbaupflug und fahrunabhängigem Mähwerk. *'''1930''' wurden die ersten Schlepper verkauft. *'''1933''' legte Joh. Georg Fendt die erste Serie auf und stellte auf der Landwirtschaftsausstellung in München aus. Erstmals konnten es sich auch klein- und mittelbäuerliche Betriebe wirtschaftlich leisten, das Pferd zu ersetzen. Sein Name war Programm: ''Fendt-Dieselroß'' *'''1937''' Das Dieselross F 18 (16 PS) konnte mit einer fahrunabhängigen und lastschaltbaren Zapfwelle ausgestattet werden. *'''1938''' wurde der 1000ste Dieselroß-Traktor, ein F 18 mit 16 PS, fertiggestellt. Die Form des Dieselross F 22 (22 PS) erregte mit dem stehenden Motor und Kühler-Ventilator Aufsehen bei den Landwirten. Das Dieselroß F 22 hatte schon eine Reihe von Merkmalen der Traktoren der 1950er-Jahre: Zweizylinder-Motor, stehender Kühler und 4-Gang-Getriebe. *[[Datei:1928 - Fendt - Grasmäher 4PS .tiff-crop.png|mini|Fendt Grasmäher, 4 PS (1928)]]'''1942''' führte Dieselöl-Knappheit und das Verbot des Einsatzes von Dieselschleppern zur Entwicklung des Holzgasgenerator-Schleppers mit 25 PS. Nach dem Zweiten Weltkrieg expandierte das Unternehmen unter der Leitung Xaver Fendts und seiner jüngeren Brüder Hermann und Paul Fendt ungewöhnlich rasch. [[Datei:Fendt Dieselross F17 WG, 18 PS, Bj. 1959 (2012-07-18).JPG|mini|hochkant=1.2|Fendt Dieselross F 17 WG von 1959, im Jahr 2012 noch bei der Pflege von öffentlichen Anlagen im Einsatz]] *'''1949''' Vom Dieselross F 18 (18 PS) wurden monatlich 20 Stück produziert. *'''1952''' wurde das 12 PS starke Dieselroß F 12 L vorgestellt, das ab 1953 in Großserie ging. Im Jahre *'''1953''' kam der 12 PS starke Fendt-Geräteträger mit vier Anbauräumen auf den Markt, dem 1959 für sein geniales Einmann-System die höchste DLG-Anerkennung verliehen wurde. *'''1957''' wurde mit der Serienfertigung des [[Traktorenlexikon: Fendt#GT (Geräteträger)|Geräteträgers]] begonnen. *'''1958''' hat man das aktuelle Programm durch die neue "ff"-Reihe mit den Typen [[#Fix|Fix]], [[#Farmer|Farmer]] und [[#Favorit|Favorit]] abgelöst. Dabei wurde das Design geändert, und die Motorleistung von 15 bis 80 PS angeboten. Der [[#Favorit 1|Favorit 1]] war richtungweisend in Form und technischer Ausstattung, wie zum Beispiel der 40 PS-Motor und das Vielgang-Feinstufengetriebe. *'''1961''' konnte bereits der 100.000ste Fendt, ein "Farmer 2" mit 35 PS, fertiggestellt werden. Besonders erfolgreich entwickelten sich die Geräteträger, ein Mechanisierungssystem von der Saat bis zur Ernte, von denen bisher rund 60.000 Stück abgesetzt werden konnten. *'''1962''' Der erste selbstfahrende Rübenvollernter auf der Basis des Fendt-GT-Einmann-Systems wurde mit der höchsten DLG-Auszeichnung prämiert. *'''1968''' Farmer 3 S (48 PS) mit stufenloser Anfahrautomatik (Turbomatik) – seine Technik wurde zum Vorbild. *'''1970''' übernahm Fendt Teile der Firma [[Traktorenlexikon: Dechentreiter|Lely-Dechentreiter]] *'''1971''' Mit dem Universaltransporter und Erntefahrzeug Agrobil S stellte Fendt 1971 eine landtechnische Neuheit vor. *'''1975''' Spezialtraktoren für Wein-, Hopfen- und Obstbau (42, 50 und 65 PS). *'''1976''' Ein neues Marktsegment eröffneten die vorgestellten Favorit-Großtraktoren mit bis zu 150 PS. *'''1980''' brachte Fendt die Farmer 300er-Reihe auf den Markt, die mit technischen Pionierleistungen wie 40 km/h und gummigelagerter Kabine maßgeblich zu den Markterfolgen der letzten Jahre beigetragen hat. * „Wer besser sieht, kann besser arbeiten“ – nach diesem Motto hat Fendt den 380 GTA als Original-Freisicht-Traktor ohne Motorhaube im Jahre '''1984''' realisiert. Sein Geheimnis ist das geniale Konzept mit Unterflurmotor. *'''1985''' wird Fendt erstmals Marktführer in Deutschland. *'''1987''' High-Tech in kompakter Dimension zeichnete die 200er Baureihe von 40 bis 75 PS aus. Als Standard- oder Spezialtraktor für den Wein- und Obstbau. *'''1993''' Die 800er sind die ersten Großtraktoren der Welt mit Turboshift, hydropneumatischer Kabinen- und Vorderachsfederung und 50 km/h. Die leistungsstärkste Maschine: Favorit 824 mit 230 PS. *'''1994''' Die Favorit 500 C-Baureihe (94 bis 140 PS) - integriert die Erfolgsmerkmale wie 50 km/h, Federung und Turboshift in der Mittelklasse; Kunden und Fachwelt sind begeistert. *'''1995''' Das wegweisende Systemfahrzeug Xylon (110 bis 140 PS) kommt auf den Markt. Ein universell einsetzbares Fahrzeug für Landwirtschaft, Landschaftspflege und Kommunen. *'''1996''' Meilenstein: Vario 926 mit 260 PS, der erste Großtraktor der Welt mit dem stufenlosen Fendt-Vario-Getriebe. Eine Revolution im Getriebebau. Als führende High-Tech- und Qualitätsmarke wird Fendt 1997 Teil des weltweit agierenden [[Traktorenlexikon: AGCO|AGCO-Konzerns]]. *'''1997''' Einführung der neuen Traktorengeneration Farmer 300 C von 75 bis 95 PS. Fendt präsentiert mit der Baureihe Favorit das Vario-Programm von 170 bis 260 PS mit stufenloser Antriebstechnik. Mit der Vorstellung der neuen Vario-Baureihe Favorit 700 mit 140 und 160 PS setzt Fendt 1998 erneut Maßstäbe für die Zukunft. Das innovative Bediensystem, die Fendt-Variotronic, wird anschließend international ausgezeichnet. *'''1998''' Der 380 GTA-Turbo mit 95 PS und der 370 GMT mit 75 PS - die Original-Freisichtschlepper *'''1999''' Mit Vario 2000 präsentiert Fendt ein komplettes Programm von 86–286 PS. Dieses Programm überträgt die Vario-Vorteile erstmals in die untere Mittelklasse bis 86 PS. ** Farmer 400 Vario von 86 bis 110 PS, die neue Dimension in der Mittelklasse ** Baureihe Favorit 700 Vario mit neuer Technik und ergänzt um die Modelle 711 (115 PS) und 712 (125 PS) ** Favorit 900 Vario, die neue Generation für schwerste Einsätze im Leistungssegment 180 bis 270 PS. Der neue Slogan: „Wer führt, fährt Vario!“ *'''2001''' Neu im Fendt- Programm ist der 300 C von 75 bis 100 PS, sowie der 412 Vario mit 120 PS *'''2002''' Farmer 200 V/P von 65 bis 94 PS ** 800 Vario TMS von 168 bis 195 PS ** 900 Vario TMS von 198 bis 310 PS ** Fendt-Mähdrescher ** Fendt-Rund- und Quaderballenpressen *'''2004''' sorgen zwei Innovationen für Aufmerksamkeit: die niveaugeregelte Vorderachsfederung für Schmalspurtraktoren und das GPS-gestützte Spurführungssystem Auto-Guide-System. *'''2005''' Vom 6-PS-Dieselross zum 900 Vario TMS mit bis zu 310 PS in 75 Jahren – dieses Jubiläum feierte Marktoberdorf gemeinsam mit Fendtlern aus aller Welt. ** 936 Vario TMS mit einer max. Leistung von 358 PS und als erster Standardtraktor mit 60-km/h-Getriebe. * 200x … ==Typen== Es wurden Schlepper mit folgenden Typenbezeichnungen vertrieben ('''die Liste ist noch unvollständig!'''): === Dieselross === [[File:Fendt Dieselross 6 PS 01.jpg|thumb|Fendt Dieselross F1 6 PS]] [[File:Fendt Dieselross F15G6 (1954).jpg|thumb|Fendt Dieselross F15 G6]] [[File:Fendt F40.jpg|thumb|Fendt Dieselross F40]] Das Fendt Dieselross ist die legendäre Ursprungsbaureihe, mit der das Unternehmen Fendt ab den 1930er Jahren den Grundstein für seinen Aufstieg zum führenden Traktorenhersteller legte. Der Name wurde 1930 mit dem ersten europäischen Kleinschlepper mit 6 PS Dieselmotor, dem F1, geprägt und stand über Jahrzehnte als Synonym für Zuverlässigkeit und technischen Fortschritt in der Landwirtschaft. Die Serie entwickelte sich von einfachen Einzylinder-Maschinen hin zu leistungsstarken Mehrzylindern und deckte bis zum Ende der Produktion im Jahr 1958 ein breites Spektrum für klein- und mittelbäuerliche Betriebe ab. Technisch waren die Dieselross-Modelle Vorreiter in vielen Bereichen. Während die frühen Typen wie das F12, F15 oder F18 noch mit Verdampferkühlung oder einfacher Luftkühlung arbeiteten, führte Fendt später moderne wassergekühlte Aggregate und innovative Getriebekonstruktionen ein. Ein Meilenstein war die Einführung der gefederten Vorderachse und der motorabhängigen Zapfwelle, die das Dieselross zu einem universell einsetzbaren Arbeitsgerät machten. Die Motoren stammten überwiegend von MWM oder wurden in Kooperation entwickelt. Charakteristisch für diese Ära war das markante Design mit der hohen, schmalen Kühlermaske und dem tiefen, oft unverkennbaren Klang der Ein- und Zweizylinder-Dieselmotoren. Die Baureihe unterteilte sich in zahlreiche Varianten, von den leichten Modellen für den Gartenbau bis hin zu den schweren Ackerschleppern wie dem F40. Mit dem Übergang zur Farmer- und Favorit-Serie Ende der 1950er Jahre wurde der Markenname Dieselross offiziell abgelöst, blieb jedoch als Ehrentitel auf den Motorhauben der frühen Farmer-Modelle erhalten. Heute gelten die Dieselross-Schlepper als die begehrtesten Sammlerobjekte der deutschen Traktorengeschichte und werden für ihre robuste Mechanik und ihre Bedeutung für die Motorisierung der Landwirtschaft geschätzt. {{Wikipedia1|Fendt Dieselross}} {| class="wikitable" | '''Dieselross''' (1928–1961) |- | valign="top" | * {{:Traktorenlexikon: Create|Grasmäher 1928|Grasmäher 1928}} * {{:Traktorenlexikon: Create|Dieselross 6 PS 1930|Dieselross 6 PS 1930}} * {{:Traktorenlexikon: Create|Dieselross F9 1930-1936|Dieselross F9 1930-1936}} * {{:Traktorenlexikon: Create|Dieselross F12 1936-1937|Dieselross F12 1936-1937}} * [[Traktorenlexikon: Fendt Dieselross F12|Dieselross F12]] 8,8 kW (12 PS) * [[Traktorenlexikon: Fendt Dieselross F15|Dieselross F15]] 11 kW (15 PS) * [[Traktorenlexikon: Fendt Dieselross F15 G6|Dieselross F15 G6]] 11 kW (15 PS) * [[Traktorenlexikon: Fendt Dieselross F17|Dieselross F17]] 12,5 kW (17 PS) * [[Traktorenlexikon: Fendt Dieselross F18|Dieselross F18]] 12/13 kW (16/18 PS) * [[Traktorenlexikon: Fendt Dieselross F18H|Dieselross F18H]] 13 kW (18 PS) * [[Traktorenlexikon: Fendt Dieselross F18 G|Dieselross F18 G]] 13/14,5 kW (18/20 PS) * [[Traktorenlexikon: Fendt Dieselross F20|Dieselross F20]] 14,5 kW (20 PS) * {{:Traktorenlexikon: Create|Dieselross F22}} 16 kW (22 PS) * [[Traktorenlexikon: Fendt Dieselross F24|Dieselross F24]] 17 kW (24 PS) * {{:Traktorenlexikon: Create|Dieselross F25}} 21 kW (25 PS) * {{:Traktorenlexikon: Create|Dieselross F25A}} 18 kW (25 PS) * [[Traktorenlexikon: Fendt Dieselross F28|Dieselross F28]] 21 kW (28 PS) * [[Traktorenlexikon: Fendt Dieselross F40|Dieselross F40]] 29 kW (40 PS) * {{:Traktorenlexikon: Create|Dieselross G25 Holzgas}} 18 kW (25 PS) * [[Traktorenlexikon: Fendt Dieselross F236|Dieselross F236]] 14,5 kW (20 PS) * [[Traktorenlexikon: Fendt Dieselross F237|Dieselross F237]] 17 kW (24 PS) * [[Traktorenlexikon: Fendt Dieselross FL114|Dieselross FL114]] 8 kW (12 PS) * [[Traktorenlexikon: Fendt Dieselross FLS237|Dieselross FLS237]] 18 kW (24 PS) |} === GT (Geräteträger) === 1953 wurde erstmals der Geräteträger F 12 GT der Öffentlichkeit auf der DLG-Ausstellung vorgestellt. Die für den Einmannbetrieb konstruierte ''selbstfahrende Arbeitsmaschine'' hatte vier Anbauräume: vor, unter, auf und hinter dem Schlepper. Durch das ''Fendt-Einmannsystem'' waren alle Anbauten ohne einen zweiten Mann möglich, und durch Vorführungen und Ausbildungskurse konnten Bauern für dieses System so begeistert werden, dass Fendt als einziger Gerätehersteller der Durchbruch in wirtschaftliche interessante Stückzahlen gelang. Hermann Fendt verwendete eine Konstruktion, die im Gegensatz zu allen anderen Herstellern nur einen Längsträger zum Anbau von Geräten hatte. Dieser Längsträger war starr mit der Vorderachse sowie drehbar mit der Schlepperhinterachse verbunden. Dadurch war es möglich, dass die angebauten Werkzeuge den Pendelbewegungen der Vorderachse folgten. 1957 wurde dieser erste Geräteträger dann zur Produktion freigegeben. Da die Motorisierung nicht ausreichte, wurde 1958 der F 220 GT vorgestellt, der einer Motorleistung von 19 PS hatte. [[Datei:Fendt F225GT (1961, 25 PS).jpg|thumb|[[Traktorenlexikon: Fendt F 225 GT|F 225 GT]]]] Da Fendt immer noch die schlechte Sicht auf die Anbaugeräte störte, wurde mit der Entwicklung eines Schleppers mit Unterflurmotor begonnen. Mit dem F 250 GT stellte man dann den ersten Schlepper mit Unterflurmotor vor. Er hatte den Motor unter dem Fahrerstand, und somit freie Sicht auf die Anbaugeräte an der Zwischenachse, da keine störende Motorhaube mehr im Sichtfeld war. In den 1970er Jahren folgten der F 255 GT und der F 275 GT, die dann eine feste Kabine besaßen. Diese gab es wahlweise mit langem Holm (GT) oder kurzem Holm (GTF). Im Jahr 1984 wurden sie dann durch die 300er Baureihe abgelöst. Der erste war der F 380 GTA (Geräteträger Allrad), und es folgte der F 360 GT/GTA sowie weitere Modelle von 50 bis 115 PS. Folgende Abkürzungen wurden verwendet: * '''GT:''' Geräteträger mit langen Holm * '''GTF:''' Geräteträger kurzer Holm (auch Kompakt GT oder Futterernte-Geräteträger genannt) * '''GTH:''' Geräteträger mit langen Holm und Hochradversion, dadurch mehr Bodenfreiheit * '''GTA:''' Allradgeräteträger * '''GHA:''' Allradgeräteträger in Hochradversion * '''GKA:''' Allradgeräteträger in Kommunalausführung (teilweise mit Großraumkabine des 390 GTA) [[Datei:Fendt F 345 GT 100 1744.jpg|thumb|[[Traktorenlexikon: Fendt F 345 GT|F 345 GT]]]] [[File:Fendt 370GT und Hürlimann D115.jpg|thumb|Fendt 370GT und Hürlimann D115]] Die Geräteträger werden heute nicht mehr produziert, da die Firma AGCO in Marktoberdorf im Jahr 2004 beschlossen hat, die Produktion der Geräteträger nach 40 Jahren aus wirtschaftlichen Gründen einzustellen. {{Wikipedia1|Fendt GT}} {| class="wikitable" | '''GT''' (1957–1958) || '''200 GT''' (1958–1993) || '''300 GT''' (1984–2004) |- | valign="top" | * [[Traktorenlexikon: Fendt F 12 GT|F 12 GT]] 9 kW (12 PS) | valign="top" | * [[Traktorenlexikon: Fendt F220 GT|F 220 GT]] 14 kW (19 PS) * [[Traktorenlexikon: Fendt F 225 GT|F 225 GT]] 18 kW (25 PS) * [[Traktorenlexikon: Fendt F 230 GT|F 230 GT]] 22 kW (30 PS) * [[Traktorenlexikon: Fendt F231 GT|F 231 GT]] 24/26 kW (32/35 PS) * [[Traktorenlexikon: Fendt F250 GT|F 250 GT]] 33 kW (45 PS) * {{:Traktorenlexikon: Create|F 255 GT}} 37/40 kW (50/55 PS) * [[Traktorenlexikon: Fendt F275 GT|F 275 GT]] 51 kW (70 PS) | valign="top" | * [[Traktorenlexikon: Fendt F 345 GT|F 345 GT]] 33 kW (45 PS) * {{:Traktorenlexikon: Create|F 350 GT|F 350 GT}} * {{:Traktorenlexikon: Create|F 360 GTA}} 44 kW (60 PS) * [[Traktorenlexikon: Fendt F365 GTA|F 365 GTA]] 48 kW (65 PS) * {{:Traktorenlexikon: Create|F 370 GT|F 370 GT}} * {{:Traktorenlexikon: Create|F 380 GTA}} 59 kW (80 PS) * {{:Traktorenlexikon: Create|F 390 GT|F 390 GT}} * [[Traktorenlexikon: Fendt F395 GTA Freisicht|F 395 GTA Freisicht]] * {{:Traktorenlexikon: Create|F 380 GTA Turbo|F 380 GTA Turbo}} |} === Fix === [[File:Fendt Fix 2 im "Ländle".jpg|thumb|Fendt Fix 2]] Die Fendt Fix Baureihe war eine Serie von Kleinschleppern, die ab Ende der 1950er Jahre als Einstiegssegment unterhalb der Farmer-Baureihe produziert wurde. Die Serie richtete sich an kleinere landwirtschaftliche Betriebe sowie an Nebenerwerbslandwirte und zeichnete sich durch eine einfache, robuste Technik aus. Zu den bekanntesten Modellen zählen der Fix 1 und der Fix 2, die in verschiedenen Varianten wie der luftgekühlten (FL) oder wassergekühlten (FW) Ausführung sowie als weiterentwickelte Versionen wie der Fix 16, Fix 2D oder Fix 2E erschienen. Technisch setzten die Fix-Traktoren überwiegend auf Motoren des Herstellers MWM (Motorenwerke Mannheim). Die Leistung der 1- und 2-Zylinder-Dieselaggregate reichte von etwa 10 kW (14 PS) beim Fix 1 bis zu 16 kW (22 PS) beim Fix 2E. Die Kraftübertragung erfolgte über ein Fendt-Gruppenschaltgetriebe mit serienmäßig sechs Vorwärts- und zwei Rückwärtsgängen, wobei für einige Modelle optional Superkriechgänge verfügbar waren. Ein markantes Merkmal war das geringe Eigengewicht von rund 1.250 kg, was die Maschinen besonders wendig machte. Die Ausstattung umfasste in der Regel eine Getriebezapfwelle, eine Differentialsperre und auf Wunsch die damals moderne Fendt-Blockhydraulik mit Raddruckverstärker zur Erhöhung der Zugkraft. Gegen Ende der 1960er Jahre wurde die Produktion der Fix-Reihe eingestellt, da der Markt für Kleinstschlepper rückläufig war und Fendt sein Portfolio mit Modellen wie dem Farmer 1E nach unten abrundete. {| class="wikitable" | '''Fix''' (1958–1970) |- | valign="top" | * [[Traktorenlexikon: Fendt Fix 1|Fix 1]] 10 kW (14 PS) * [[Traktorenlexikon: Fendt Fix 2|Fix 2]] 13-14 kW (18-19 PS) * {{:Traktorenlexikon: Create|Fix 2 D}} 12 kW (17 PS) * [[Traktorenlexikon: Fendt Fix 2 E|Fix 2 E]] 16 kW (22 PS) * [[Traktorenlexikon: Fendt Fix 16|Fix 16]] 11 kW (16 PS) |} === Farmer === ==== Farmer (erste Serie) ==== [[File:Fendt Farmer 2 (2).jpg|thumb|Fendt Farmer 2 ]] [[File:Fendt Farmer 4 S JM 2023 08 12 5D410499.jpg|thumb|Fendt Farmer 4 S (Haube ab 1968)]] Nachdem Fendt die Weichen in Richtung Zukunft gestellt hat, und 1958 mit dem Kleinschlepper ''Fix'' und dem Großschlepper ''Favorit'' schon den Einstieg in die neue Schlepper-Generation vollzogen hatte, wurde mit der neuen Farmer-Modellreihe die Schlepper-Familie vervollständigt. Hierbei fand die neu gestylte Motorhaube mit integrierten Scheinwerfern Verwendung, und mit einer Leistung ab 25 PS hatte der Fendt Farmer genau die richtige Dimensionierung, um die Gruppe von mittelgroßen Familienbetrieben zu erreichen. Der Farmer wurde von Fendt technisch sehr modern gestaltet, so ist der Farmer 3 S 1966 der erste Fendt-Schlepper, der mit einer ölhydraulischen Kupplung ausgestattet wurde, welche später unter den Namen 'Turbomatik' zum Markenzeichen von Fendt wurde. Der 3 S ist der erste Schlepper dieser Serie, der einen MWM-Vierzylinder-Motor erhält, es ist der leicht gedrosselte Motor aus dem Favorit 3. 1968 erhält er zur DLG-Ausstellung in München die eckige Motorhaube, die die Produktdesigner nunmehr für der Zeit angemessener halten. {| class="wikitable" | '''Farmer''' (1958–1973) |- | valign="top" | {{:Traktorenlexikon: Create|Farmer 1}} 18 kW (25 PS) {{:Traktorenlexikon: Create|Farmer 1 E}} 22 kW (30 PS) {{:Traktorenlexikon: Create|Farmer 1 Z}} 18 kW (25 PS) {{:Traktorenlexikon: Create|Farmer 1 D}} ---- {{:Traktorenlexikon: Create|Farmer 2}} (FW 139, 1960–1967) 25-28 kW (34-38 PS) {{:Traktorenlexikon: Create|Farmer 2 D}} (FW 228, 1961–1967) 20,5-23 kW (28-32 PS) {{:Traktorenlexikon: Create|Farmer 2 DE}} (FW 228, 1968–1971) 25 kW (35 PS) {{:Traktorenlexikon: Create|Farmer 2 E}} (FW 139, 1968–1970) 29 kW (40 PS) {{:Traktorenlexikon: Create|Farmer 2 S/SA}} (FW/FWA 138, 1968–1972) 30 kW (42 PS) {{:Traktorenlexikon: Create|Farmer 2 W}} ? ---- {{:Traktorenlexikon: Create|Farmer 3 S/SA}} (FW 238/1, FWA 238/7) 33 kW (45 PS) ---- {{:Traktorenlexikon: Create|Farmer 4 S/SA}} (FW 258/1, FWA 258/7) 40 kW (55 PS) ---- {{:Traktorenlexikon: Create|Farmer 5 S/SA}} (FW 268/01, FWA 268/21) 48 kW (65 PS) |} ==== Farmer 100 ==== [[File:Fendt Farmer 108 S Traktor (2023).jpg|thumb|Fendt Farmer 108 S]] Die Fendt Farmer 100 Serie wurde Ende der 1960er Jahre eingeführt und bildete bis in die frühen 1980er Jahre das Rückgrat des Fendt-Programms im mittleren Leistungssegment. Sie löste die erfolgreiche Farmer 1- bis 5-Reihe ab und war die erste Baureihe, die das moderne, kantige Erscheinungsbild der Marke mit der markanten, flachen Motorhaube und den eckigen Doppelscheinwerfern im Grill etablierte. Die Serie deckte mit Modellen wie dem Farmer 102 bis zum Farmer 108 ein Leistungsspektrum von etwa 31 kW (42 PS) bis 55 kW (75 PS) ab und war sowohl mit Hinterrad- als auch mit Allradantrieb erhältlich. Technisch setzten diese Schlepper Maßstäbe durch die flächendeckende Einführung der Turbomatik (Flüssigkeitskupplung) in der S-Ausführung, die ein ruckfreies Anfahren und hohen Schutz für Motor und Getriebe bot. Als Antrieb dienten überwiegend wassergekühlte Drei- und Vierzylinder-Dieselmotoren von MWM, die für ihre Sparsamkeit und Zuverlässigkeit bekannt waren. Das Getriebe verfügte in der Regel über ein Gruppenkonzept mit 13 Vorwärts- und 4 Rückwärtsgängen, was eine für die damalige Zeit sehr feine Abstimmung ermöglichte. Ein besonderes Merkmal war die Vielseitigkeit der Serie: Sie war als robuster Ackerschlepper konzipiert, eignete sich aber aufgrund ihrer kompakten Maße und Wendigkeit ebenso für Hof- und Pflegearbeiten. Die Farmer 100 Serie festigte Fendts Ruf als Premiumhersteller und legte die technische Basis für die 1980 folgende, legendäre Farmer 300 Serie. {| class="wikitable" | '''Farmer 100''' (1972–1980) |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 102}} 29 kW (40 PS) * {{:Traktorenlexikon: Create|Farmer 102 S/SA}} 31 kW (42 PS) * {{:Traktorenlexikon: Create|Farmer 102 LS/LSA}} 35 kW (48 PS) * {{:Traktorenlexikon: Create|Farmer 103}} 35 kW (48 PS) * {{:Traktorenlexikon: Create|Farmer 103 S/SA}} 35 kW (48 PS) * {{:Traktorenlexikon: Create|Farmer 103 LS/LSA}} 35 kW (48 PS) * {{:Traktorenlexikon: Create|Farmer 104 S/SA}} 40 kW (55/60 PS) * {{:Traktorenlexikon: Create|Farmer 104 LS/LSA}} 40 kW (55 PS) * {{:Traktorenlexikon: Create|Farmer 105}} 44kW (60 PS) * {{:Traktorenlexikon: Create|Farmer 105 S/SA}} 55,2 kW (75 PS) * {{:Traktorenlexikon: Create|Farmer 105 LS/LSA}} 44kW (60 PS) * {{:Traktorenlexikon: Create|Farmer 106 S/SA}} 48 kW (65 PS) * {{:Traktorenlexikon: Create|Farmer 106 LS/LSA}} 48 kW (65 PS) * {{:Traktorenlexikon: Create|Farmer 108 S/SA}} 55 kW (75 PS) * {{:Traktorenlexikon: Create|Farmer 108 LS/LSA}} 55 kW (75 PS) |} ==== Farmer 200 ==== [[File:Agricultural Liechtenstein license plate old style.jpg|thumb|Fendt Farmer 240 S]] Die Fendt Farmer 200 Serie wurde ab 1974 als spezialisierte Baureihe für Betriebe mit begrenzten Platzverhältnissen sowie für den Einsatz im Grünland und im Nebenerwerb eingeführt. Sie besetzte die Nische zwischen den Kleinschleppern der Fix-Reihe und den größeren Modellen der Farmer 300 Serie und zeichnete sich durch eine besonders kompakte Bauweise, eine niedrige Gesamthöhe und eine hohe Wendigkeit aus. Technisch unterschieden sich die frühen Modelle, wie der Farmer 200 S bis 205 P, durch den Einsatz von luftgekühlten Deutz-Motoren, was eine kompaktere Konstruktion ermöglichte. Im Gegensatz zur größeren 300er-Serie wurde in der 200er-Reihe zunächst auf die Turbomatik verzichtet, um Bauraum und Gewicht zu sparen, wobei das Getriebekonzept speziell auf die Anforderungen im alpinen Raum und in Sonderkulturen abgestimmt war. Die Serie unterteilte sich in verschiedene spezialisierte Ausführungen, darunter die Standard-Versionen für den allgemeinen landwirtschaftlichen Einsatz sowie die Schmalspur-Varianten V und P für den Wein- und Obstbau. In den 1980er und 1990er Jahren folgte eine kontinuierliche Modernisierung, bei der die Motorhauben optisch an das Design der größeren Baureihen angepasst wurden und vermehrt wassergekühlte Aggregate Einzug hielten. Trotz der geringeren Abmessungen bot die 200er Serie eine vollwertige Ausstattung mit Zapfwellen und leistungsfähiger Hydraulik, was sie zum idealen Werkzeug für spezialisierte Betriebe machte. Im Jahr 2003 endete die Ära der klassischen Farmer 200 Serie mit der Einführung des Fendt 200 Vario, der als weltweit erster Spezialtraktor mit stufenlosem Getriebe die Tradition der kompakten Hochleistungsschlepper fortsetzte. {| class="wikitable" | '''Farmer 200''' (1974–2003) |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 200 S/SA}} 26 kW (35 PS) * {{:Traktorenlexikon: Create|Farmer 201 S}} 31/33 kW (42/45 PS) * {{:Traktorenlexikon: Create|Farmer 204 P}} 48 kW (65 PS) * {{:Traktorenlexikon: Create|Farmer 206 S}} * {{:Traktorenlexikon: Create|Farmer 207 S}} * {{:Traktorenlexikon: Create|Farmer 208 S}} 63 kW (86 PS) * {{:Traktorenlexikon: Create|Farmer 209 S}} * {{:Traktorenlexikon: Create|Farmer 240 S}} * {{:Traktorenlexikon: Create|Farmer 250 S/SA}} * {{:Traktorenlexikon: Create|Farmer 260 P}} * {{:Traktorenlexikon: Create|Farmer 260 S/SA}} 44 kW (60 PS) * {{:Traktorenlexikon: Create|Farmer 275 S/SA}} * {{:Traktorenlexikon: Create|Farmer 280 S}} |} ===Schmalspur-Schlepper=== [[File:Fendt Farmer 203 P II Traktor.jpg|thumb|Fendt Farmer 203 P]] Die Fendt Farmer Schmalspurtraktoren wurden speziell für den Einsatz in Sonderkulturen wie Wein-, Obst- und Hopfenbau entwickelt, wo extrem schmale Fahrgassen eine minimale Außenbreite der Maschine erfordern. Die Baureihe wurde ab den 1970er Jahren parallel zur Standard-200er-Serie geführt und durch die Kennbuchstaben V (Vineyard/Weinbau) und P (Plantage/Obstbau) unterschieden. Während die V-Modelle mit einer Außenbreite von teilweise unter einem Meter für engste Rebanlagen optimiert waren, boten die etwas breiteren P-Modelle mehr Standfestigkeit für Arbeiten in Obstplantagen oder im Hopfenanbau. Technisch basierten die Schmalspurschlepper zunächst auf luftgekühlten Deutz-Motoren, um die kompakten Abmessungen und die erforderliche Wendigkeit zu realisieren. Ein wesentliches Merkmal war der extrem enge Radstand und die spezielle Vorderachskonstruktion, die einen hohen Lenkeinschlag ermöglichte. Trotz der geringen Größe waren die Maschinen voll ausgestattete Traktoren mit leistungsfähiger Hydraulik, mehreren Zapfwellengeschwindigkeiten und – in späteren Generationen – komfortablen, schmalen Kabinen inklusive Filteranlagen für den Pflanzenschutz. In den 1990er Jahren wurden auch in diesem Segment wassergekühlte Motoren und modernere Getriebe zum Standard, bevor die Baureihe 2009 durch den 200 Vario V/F/P ersetzt wurde, der als erster Schmalspurtraktor der Welt mit einem stufenlosen Getriebe die Marktführerschaft in diesem spezialisierten Segment festigte. {| class="wikitable" | '''Farmer 200 V/F/P''' |- | valign="top" | * {{:Traktorenlexikon: Create|FLS 237}} * {{:Traktorenlexikon: Create|Farmer 2 W}} * {{:Traktorenlexikon: Create|Farmer 200 V}} * {{:Traktorenlexikon: Create|Farmer 203 V/VA}} * {{:Traktorenlexikon: Create|Farmer 203 V/VA-II}} * {{:Traktorenlexikon: Create|Farmer 204 V/VA}} * {{:Traktorenlexikon: Create|Farmer 250 V/VA}} * {{:Traktorenlexikon: Create|Farmer 260 V/VA}} * {{:Traktorenlexikon: Create|Farmer 270 V/VA}} * {{:Traktorenlexikon: Create|Farmer 275 V/VA}} * {{:Traktorenlexikon: Create|Farmer 280 VA}} * {{:Traktorenlexikon: Create|Farmer 203 P/PA}} * {{:Traktorenlexikon: Create|Farmer 203 P/PA-II}} * {{:Traktorenlexikon: Create|Farmer 204 P}} * {{:Traktorenlexikon: Create|Farmer 205 P/PA}} * {{:Traktorenlexikon: Create|Farmer 260 P/PA}} * {{:Traktorenlexikon: Create|Farmer 270 P/PA}} * {{:Traktorenlexikon: Create|Farmer 280 P/PA}} * {{:Traktorenlexikon: Create|Farmer 206 F/FA}} * {{:Traktorenlexikon: Create|Farmer 207 F/FA}} * {{:Traktorenlexikon: Create|Farmer 208 V/VA}} * {{:Traktorenlexikon: Create|Farmer 208 FA}} * {{:Traktorenlexikon: Create|Farmer 208 PA}} * {{:Traktorenlexikon: Create|Farmer 209 VA}} * {{:Traktorenlexikon: Create|Farmer 209 FA}} * {{:Traktorenlexikon: Create|Farmer 209 PA}} |} ====Farmer 300==== [[File:Fendt Farmer 307LSA Turbomatik.jpg|thumb|Fendt Farmer 307LSA Turbomatik]] Die Fendt Farmer 300 Serie gilt als eine der erfolgreichsten und prägendsten Traktorenbaureihen der europäischen Landtechnikgeschichte. Eingeführt im Jahr 1980, blieb die Baureihe in verschiedenen Evolutionsstufen über einen Zeitraum von fast drei Jahrzehnten in Produktion und deckte als vielseitiger Allrounder das mittlere Leistungssegment ab. Ein zentrales technisches Merkmal war die Integration der Fendt-Turbomatik, einer Flüssigkeitskupplung, die ein verschleißfreies Anfahren ermöglichte und den Antriebsstrang schonte. In der ersten Generation, den LS- und LSA-Modellen, wurden überwiegend wassergekühlte 3-, 4- und 6-Zylinder-Motoren von MWM verbaut, die ein Leistungsspektrum von etwa 43 kW (58 PS) bis 92 kW (125 PS) abdeckten. Im Laufe der Jahre wurde die Serie kontinuierlich modernisiert, wobei insbesondere die Einführung der 40-km/h-Varianten mit serienmäßiger Vierradbremse und gefederter Vorderachse Maßstäbe in der Mittelklasse setzte. Mit der umfassenden Modellpflege im Jahr 1993 erhielten die Schlepper eine deutlich leisere und ergonomisch optimierte Kabine sowie eine abgerundete Motorhaube. Das Getriebe wurde stetig weiterentwickelt, von den klassischen 21/6-Gang-Varianten bis hin zu Versionen mit elektrohydraulischer Wendeschaltung. In der finalen Phase der Baureihe, den C- und Ci-Modellen ab Ende der 1990er Jahre, kamen verstärkt Motoren von Deutz zum Einsatz. Aufgrund ihrer extremen Robustheit, der hohen Wertbeständigkeit und der einfachen Handhabung ist die Farmer 300 Serie bis heute auf vielen Betrieben im aktiven Einsatz. Erst im Jahr 2006 wurde die Baureihe durch den technologisch völlig neu konzipierten Fendt 300 Vario mit stufenlosem Antrieb abgelöst. [[File:Fendt Farmer 305 LS.jpg|thumb|Fendt Farmer 305 LS]] Die Bezeichnung '''LS''' steht für „Luxus-Standard“ und kennzeichnete innerhalb der Farmer 300-Serie die Modelle mit Hinterradantrieb. Diese Ausführung wurde vor allem für Betriebe konzipiert, die einen wendigen und leichten Schlepper für Pflegearbeiten, leichte Transportaufgaben oder den Einsatz im Grünland benötigten, bei denen die zusätzliche Zugkraft eines Allradantriebs nicht zwingend erforderlich war. Trotz des fehlenden Allradantriebs bot die LS-Variante den vollen Komfort der neu entwickelten, gummigelagerten Sicherheitskabine, die den Lärmpegel für den Fahrer massiv senkte. Technisch waren diese Maschinen mit der bewährten Turbomatik-Flüssigkeitskupplung und dem feingestuften 21/6-Gang-Getriebe ausgestattet. Durch das geringere Eigengewicht an der Vorderachse und den größeren Einschlagwinkel der Vorderräder war die LS-Version in ihrer Manövrierfähigkeit den Allradmodellen überlegen, was sie zu einer beliebten Wahl für Hof- und Stallarbeiten machte. {| class="wikitable" | '''Farmer 300 LS''' (1980–1984) |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 303 LS}} 38 kW (52 PS) * {{:Traktorenlexikon: Create|Farmer 304 LS}} 43 kW (58 PS) 1982-1989 * {{:Traktorenlexikon: Create|Farmer 304 LS/Turbo}} 1990-1998 * {{:Traktorenlexikon: Create|Farmer 305 LS}} 46 kW (63 PS) * {{:Traktorenlexikon: Create|Farmer 306 LS}} 51 kW (70 PS) * {{:Traktorenlexikon: Create|Farmer 307 LS}} 51 kW (70 PS) 1988-1998 * {{:Traktorenlexikon: Create|Farmer 308 LS}} 57 kW (78 PS) * {{:Traktorenlexikon: Create|Farmer 309 LS}} 63 kW (86 PS) |} [[File:Fendt Farmer 307 LSA Turbomatik.jpg|thumb|Fendt Farmer 307 LSA Turbomatik]] Das Kürzel '''LSA''' steht für „Luxus-Standard-Allrad“ und markierte den technologischen Standard für leistungsstarke Universaltraktoren ab den 1980er Jahren. Im Gegensatz zur LS-Variante verfügten diese Modelle über einen optimierten Allradantrieb, der durch eine zentrale Gelenkwelle und ein unter Last zuschaltbares Lamellendifferential an der Vorderachse bestach. Die LSA-Modelle wurden schnell zum Rückgrat der mittelständischen Landwirtschaft, da sie die Motorleistung der 3-, 4- und 6-Zylinder-MWM-Motoren auch unter schwierigen Bedingungen effizient in Zugkraft umsetzen konnten. Neben der markanten Turbomatik und der komfortablen Kabine waren die LSA-Versionen häufig die Vorreiter für technische Neuerungen wie die 40-km/h-Zulassung und die elektronische Hubwerksregelung (EHR). Aufgrund ihrer Vielseitigkeit und der hohen Standfestigkeit bei schweren Acker- und Frontladerarbeiten entwickelten sich die LSA-Modelle zu den meistverkauften Varianten der Farmer-Reihe und gelten heute als Inbegriff des klassischen Fendt-Schleppers dieser Ära. {| class="wikitable" | '''Farmer 300 LSA''' (1984–1990, 1991–1993) |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 303 LSA}} 38 kW (52 PS) * {{:Traktorenlexikon: Create|Farmer 304 LSA}} 43 kW (58 PS) 1982-1989 * {{:Traktorenlexikon: Create|Farmer 304 LSA/Turbo}} 1990-1998 * {{:Traktorenlexikon: Create|Farmer 305 LSA}} 46 kW (63 PS) * {{:Traktorenlexikon: Create|Farmer 306 LSA}} 51 kW (70 PS) * {{:Traktorenlexikon: Create|Farmer 307 LSA}} 51 kW (70 PS) 1988-1998 * {{:Traktorenlexikon: Create|Farmer 308 LSA}} 57 kW (78 PS) * {{:Traktorenlexikon: Create|Farmer 309 LSA}} 63/66 kW (86/90 PS) * {{:Traktorenlexikon: Create|Farmer 310 LSA}} 68 kW (92 PS) * {{:Traktorenlexikon: Create|Farmer 311 LSA}} 74 kW (100 PS) * {{:Traktorenlexikon: Create|Farmer 312 LSA}} 85 kW (115 PS) |} [[File:Fendt 308.jpg|thumb|Fendt Farmer 308]] In der Periode von 1993 bis 1996 erlebte die Fendt '''Farmer 300''' Serie (modellspezifisch die Typen 307 bis 312) ihre technisch und optisch ausgereifteste Phase, bevor die Baureihe in die C- und Ci-Generationen aufgeteilt wurde. Diese Jahre markierten den Höhepunkt der klassischen 300er-Reihe, die nun serienmäßig das neue, abgerundete Design der Motorhaube und die markante, nach oben gezogene Frontscheibe erhielt. Technisch setzten diese Modelle weiterhin auf die bewährten wassergekühlten MWM-Motoren, die für ihre Langlebigkeit bekannt waren. Ein entscheidender Fortschritt dieser Jahre war die Einführung der elektrohydraulischen Wendeschaltung (EHS) und der verbesserten Komfortkabine, die mit einem Geräuschpegel von nur 72 dB(A) zu den leisesten auf dem Markt gehörte. Zudem wurde in diesem Zeitraum die gefederte Vorderachse für die größeren Modelle verfügbar, was in Kombination mit der 40-km/h-Zulassung und der echten Vierradbremse den Fahrkomfort erheblich steigerte. Diese Generation gilt heute als besonders begehrt, da sie die robuste, weitgehend mechanische Technik der Turbomatik-Ära mit einem modernen Bedienkomfort vereinte, der bereits viele Merkmale der späteren High-Tech-Schlepper vorwegnahm. {| class="wikitable" | '''Farmer 300''' (1993–1996) |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 307}} 55 kW (75 PS) * {{:Traktorenlexikon: Create|Farmer 308}} 63 kW (86 PS) * {{:Traktorenlexikon: Create|Farmer 309}} 70 kW (95 PS) * {{:Traktorenlexikon: Create|Farmer 310}} 77 kW (105 PS) * {{:Traktorenlexikon: Create|Farmer 311}} 85 kW (115 PS) * {{:Traktorenlexikon: Create|Farmer 312}} 92 KW (125PS) |} [[File:Fendt Farmer 308C.jpg|thumb|Fendt Farmer 308C]] Die '''Farmer 300 C'''-Serie wurde Ende der 1990er Jahre als technisch vereinfachte, aber moderne Alternative eingeführt. Sie sollte die Lücke schließen, die durch die zunehmende Komplexität der größeren Baureihen entstand. Ein markanter Unterschied war der Wechsel der Motorenlieferanten: Statt MWM kamen nun kompakte Deutz-Motoren zum Einsatz. Die C-Serie verzichtete auf einige elektronische Features der High-End-Modelle, bot jedoch mit der 40-km/h-Zulassung und einer kompakten Bauweise ideale Voraussetzungen für Grünlandbetriebe und Hofarbeiten. {| class="wikitable" | '''Farmer 300 C''' |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 307 C}} 55 kW (75 PS) * {{:Traktorenlexikon: Create|Farmer 308 C}} 63 kW (86 PS) * {{:Traktorenlexikon: Create|Farmer 309 C}} 70 kW (95 PS) |} [[Datei:Fendt 308 CI.jpg|thumb|Farmer 308 Ci]] Die '''Farmer 300 Ci'''-Serie stellte ab 2003 die finale Evolutionsstufe der Farmer-Reihe mit klassischem Schaltgetriebe dar. Sie basierte auf der C-Serie, wurde jedoch technisch nochmals verfeinert, um strengere Abgasnormen zu erfüllen und den Bedienkomfort (z. B. durch eine verbesserte Kabinenergonomie) zu steigern. Mit Modellen wie dem 309 Ci blieb Fendt der mechanischen Kraftübertragung treu, während größere Serien bereits auf Vario-Getriebe umgestellt wurden. Die Produktion endete 2008, womit die Ära der „Farmer“-Baureihe nach über 50 Jahren zugunsten der rein stufenlosen 300 Vario-Modelle abgeschlossen wurde. {| class="wikitable" | '''Farmer 300 Ci''' |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 307 Ci}} 68 kW (92 PS) * {{:Traktorenlexikon: Create|Farmer 308 Ci}} 73 kW (98 PS) * {{:Traktorenlexikon: Create|Farmer 309 Ci}} 83 kW (112PS) |} ==== Farmer 400 Vario==== [[File:Veghel opendag abemec 18 12 05 (71).JPG|thumb|Fendt Farmer 411 Vario]] Die Fendt Farmer 400 Vario Serie wurde im Jahr 1999 als kompakte Ergänzung zur größeren 700er-Serie eingeführt und markierte den Einzug der stufenlosen Getriebetechnik in das Segment der mittleren Kompaktschlepper. Sie löste die leistungsstärkeren Modelle der Farmer 300 Serie ab und umfasste die Typen 409, 410, 411 und 412 Vario, die einen Leistungsbereich von 70 kW (95 PS) bis 92 kW (125 PS) abdeckten. Damit war die 400er-Reihe die erste Baureihe unterhalb der 100-PS-Grenze, die weltweit mit einem stufenlosen Antrieb ausgestattet wurde. Das technische Herzstück war das ML-75-Vario-Getriebe, das ein vollkommen ruckfreies Fahren und Arbeiten von 0,02 bis 50 km/h ermöglichte. Als Kraftquelle dienten wassergekühlte 4-Zylinder-Turbomotoren von Deutz mit 3,8 Litern Hubraum und Ladeluftkühlung, die sich durch eine kompakte Bauweise und hohe Spritzigkeit auszeichneten. Trotz der geringen Abmessungen verfügte die Serie über die vollwertige High-Tech-Ausstattung der größeren Modelle, einschließlich des Varioterminals zur Steuerung von Hydraulik und Getriebemanagement sowie der optionalen Variotronic für das Vorgewendemanagement. Ein besonderes Merkmal der Farmer 400 Vario Serie war ihre enorme Wendigkeit bei gleichzeitig hohem Komfortniveau, das durch die serienmäßige Vorderachsfederung und eine komfortable Kabinenfederung erreicht wurde. Durch den kurzen Radstand und das relativ geringe Eigengewicht war sie prädestiniert für Frontladerarbeiten, den Einsatz im Grünland und als Pflegeschlepper in Ackerbaubetrieben. Mit der Überarbeitung im Jahr 2006 entfiel der Name „Farmer“, und die Serie wurde als Fendt 400 Vario (mit Leistungen bis 155 PS) weitergeführt, bis sie schließlich durch die 500 Vario Serie ersetzt wurde. Aufgrund ihrer Zuverlässigkeit und universellen Einsetzbarkeit gilt die 400er-Reihe heute als einer der wertstabilsten Gebrauchtschlepper ihrer Klasse. {| class="wikitable" | '''Farmer 400 Vario''' |- | valign="top" | * {{:Traktorenlexikon: Create|Farmer 307 Ci}} 68 kW (92 PS) * {{:Traktorenlexikon: Create|Farmer 308 Ci}} 73 kW (98 PS) * {{:Traktorenlexikon: Create|Farmer 309 Ci}} 83 kW (112PS) |} === Favorit === Die Fendt Favorit Serie bildete über Jahrzehnte das technologische Flaggschiff des Allgäuer Herstellers und repräsentierte das Segment der schweren Großtraktoren. Die Baureihe wurde bereits in den 1950er Jahren als leistungsstarke Ergänzung zu den kleineren Dieselross- und Farmer-Modellen eingeführt und durchlief eine bemerkenswerte technische Evolution von der mechanischen Ära bis hin zur Hochleistungselektronik. Während die frühen Generationen wie der Favorit 1, 2 und 3 ab 1958 vor allem durch Robustheit und Motoren von MWM im Bereich von 30 bis 40 kW (40–55 PS) überzeugten, setzten die späteren Generationen völlig neue Branchenstandards. Ein Meilenstein war die 1976 eingeführte Serie 600 (LS/LSA), die mit Leistungen bis zu 136 kW (185 PS) und der serienmäßigen Turbomatik-Flüssigkeitskupplung die Oberklasse dominierte. Diese Sechszylindermaschinen galten aufgrund ihrer massiven Bauweise und enormen Zugkraft als unzerstörbar. In den 1990er Jahren folgte mit der Baureihe 800 und 900 der Sprung in die moderne Hochleistungstechnik. Besonders der 1993 vorgestellte Favorit 800 und der 1996 erschienene Favorit 900 Vario schrieben Technikgeschichte; letzterer war der weltweit erste Großtraktor mit dem stufenlosen Vario-Getriebe, das die Art der Kraftübertragung in der Landwirtschaft revolutionierte. In ihrer finalen Phase zeichnete sich die Favorit-Serie durch Merkmale wie die gefederte Vorderachse, Kabinenfederung und eine umfassende elektronische Steuerung aus. Die Modelle, allen voran der legendäre 926 Vario, festigten den Ruf von Fendt als Premiummarke im Profisegment. Mit der vollständigen Umstellung des gesamten Portfolios auf den stufenlosen Antrieb entfiel die Zusatzbezeichnung „Favorit“ schließlich in den frühen 2000er Jahren, wobei die Leistungsmerkmale und die Positionierung als High-End-Großtraktor direkt in den heutigen Fendt 800 und 900 Vario Serien fortgeführt wurden. ==== Favorit (erste Reihe) ==== [[File:Oldtimerumzug Aidenbach 2013-08-18 - Fendt Favorit 3.JPG|thumb|Fendt Favorit 3]] [[File:Fendt Favorit 4S 127PS 1967.jpg|thumb|Fendt Favorit 4S]] Die erste Serie der Fendt Favorit-Baureihe markierte ab dem Jahr 1958 den Einstieg des Herstellers in das Segment der schweren Ackerschlepper. Sie wurde als leistungsstarke Ergänzung zu den kleineren Dieselross- und Farmer-Modellen konzipiert, um den steigenden Anforderungen größerer landwirtschaftlicher Betriebe gerecht zu werden. Die Baureihe startete mit dem Favorit 1 (Typ FW 140), der von einem wassergekühlten MWM-Dreizylinder-Dieselmotor mit einer Leistung von 29 kW (40 PS) angetrieben wurde. Kurz darauf folgten die Modelle Favorit 2 mit 33 kW (45 PS) und schließlich der Favorit 3, der ab 1964 mit einem Vierzylindermotor und einer Leistung von bis zu 38 kW (52 PS) die Baureihe nach oben abrundete. Ein technisches Kernstück dieser ersten Generation war das ZF-Gruppengetriebe (Zahnradfabrik Friedrichshafen), das in der Regel über zehn Vorwärts- und zwei Rückwärtsgänge verfügte und eine präzise Abstimmung der Arbeitsgeschwindigkeit ermöglichte. Das Design der ersten Favorit-Schlepper war durch eine markante, langgezogene Motorhaube und die damals für Fendt typische grüne Lackierung mit roten Felgen geprägt. Zur Ausstattung gehörten bereits eine moderne Motorzapfwelle sowie eine leistungsfähige Hydraulik, die den Einsatz schwerer Anbaugeräte erlaubte. Besonders der Favorit 3 setzte mit seiner robusten Bauweise und der optionalen Allradversion Maßstäbe in Sachen Zugkraft und Zuverlässigkeit. Diese erste Serie legte den Grundstein für den exzellenten Ruf der Favorit-Reihe als Hochleistungstraktoren und ebnete den Weg für die späteren Sechszylinder-Großschlepper der 600er Serie. {| class="wikitable" | '''Favorit''' (1958–1972) |- | valign="top" | * {{:Traktorenlexikon: Create|Favorit 1}} (FW 140/1, FW 140/3) 29 kW (40 PS) * {{:Traktorenlexikon: Create|Favorit 2}} (FW 150/1, FW 150/3) 33 kW (46 PS) * {{:Traktorenlexikon: Create|Favorit 3}} (FW 150/11, FWA 150/7) 38-40 kW (52-55 PS) * {{:Traktorenlexikon: Create|Favorit 3 S/SA}} (FW 165/1, FWA 165/7) 45,4 kW (62 PS) * {{:Traktorenlexikon: Create|Favorit 4}} (FW 180/3, FWA 180/7) 59 kW (80 PS) * {{:Traktorenlexikon: Create|Favorit 4 S/SA}} (FW 180/3, FWA 180/7) 66 kW (90 PS) * {{:Traktorenlexikon: Create|Favorit 10 S/SA}} (FW 181/01, FWA 181/21) 58 kW (80 PS) * {{:Traktorenlexikon: Create|Favorit 11 S/SA}} (FW 182/01, FWA 182/21) 69 kW (95 PS) * {{:Traktorenlexikon: Create|Favorit 12 SA}} (FWA 183/21) 80 kW (110 PS) |} ==== Favorit 500 ==== [[File:Fendt Favorit 515 C Turboshift (2023).jpg|thumb|Fendt Favorit 515 C]] Die Fendt Favorit 500 Serie wurde 1993 eingeführt und gilt bis heute als eine der fortschrittlichsten und vielseitigsten Traktorengenerationen, die Fendt je produziert hat. Sie schloss die Lücke zwischen der kompakten Farmer 300 Serie und den Großschleppern der Favorit 800 Serie. Mit einem Leistungsspektrum von 70 kW (95 PS) beim Favorit 509 C bis zu 110 kW (150 PS) beim Favorit 515 C vereinte die Baureihe erstmals die Wendigkeit eines Mittelklasseschleppers mit der Technik und Leistungsfähigkeit der Oberklasse. Technisch basierte die Serie auf wassergekühlten 4- und 6-Zylinder-Turbomotoren von MWM, die für ihre hohe Durchzugskraft bekannt waren. Ein herausragendes Merkmal war das Turboshift-Getriebe, das in Kombination mit der bewährten Turbomatik-Flüssigkeitskupplung ein extrem komfortables Fahren ermöglichte. Das Getriebe verfügte über vier Lastschaltstufen und eine elektrohydraulische Wendeschaltung, was insgesamt 44 Vorwärts- und 44 Rückwärtsgänge ergab. Damit waren die Traktoren bereits serienmäßig für eine Höchstgeschwindigkeit von 50 km/h ausgelegt. Zudem war die Serie 500 ein Pionier in Sachen Federungskomfort: Die Kombination aus gefederter Vorderachse und einer dreifach gummigelagerten Komfortkabine setzte neue Maßstäbe in der Branche. Die Kabine selbst bot eine moderne Ergonomie mit einer übersichtlichen Seitenkonsole, auf der viele Funktionen elektrohydraulisch gesteuert werden konnten. Dank der leistungsstarken Hydraulikanlage und der hohen Hubkraft waren die 500er-Modelle sowohl für schwere Ackerarbeiten als auch für Transport- und Frontladeraufgaben prädestiniert. Aufgrund ihrer enormen Vielseitigkeit und der robusten, langlebigen Technik erzielen gebrauchte Modelle der Favorit 500 Serie heute noch außergewöhnlich hohe Wiederverkaufswerte. Die Baureihe wurde Ende der 1990er Jahre schließlich durch die ersten stufenlosen Modelle der 700 Vario Serie ersetzt. {| class="wikitable" | '''Favorit 500''' (1993–2000) |- | valign="top" | * {{:Traktorenlexikon: Create|Favorit 509 C}} 70 kW (95 PS) * {{:Traktorenlexikon: Create|Favorit 510 C}} 77 kW (105 PS) * {{:Traktorenlexikon: Create|Favorit 511 C}} 85 kW (115 PS) * {{:Traktorenlexikon: Create|Favorit 512 C}} 93 kW (125 PS) * {{:Traktorenlexikon: Create|Favorit 514 C}} 103 kW (140 PS) * {{:Traktorenlexikon: Create|Favorit 515 C}} (FWA 515) 110 kW (150 PS) |} ==== Favorit 600 ==== [[File:Fendt Favorit 614 LS Turbomatik.jpg|thumb|Fendt Favorit 614 LS Turbomatik]] Die Fendt Favorit 600 Serie (insbesondere die LS- und LSA-Modelle) war die dominierende Großtraktorenbaureihe von Fendt in den 1970er, 80er und frühen 90er Jahren. Sie festigte den Ruf der Marke als Premiumhersteller im schweren Leistungssegment und war für ihre enorme Zugkraft sowie die hohe technische Zuverlässigkeit bekannt. Die Baureihe umfasste eine Vielzahl von Modellen, angefangen beim 600 LS (85 PS) bis hin zum legendären Spitzenmodell 615 LSA, das in seiner letzten Ausbaustufe bis zu 136 kW (185 PS) leistete. Eine technische Besonderheit und ein Alleinstellungsmerkmal in dieser Klasse war die serienmäßige Turbomatik-Flüssigkeitskupplung, die ein verschleißfreies Anfahren ermöglichte und den gesamten Antriebsstrang vor Überlastung schützte. Technisch setzten die 600er-Modelle konsequent auf wassergekühlte Sechszylinder-Dieselmotoren von MWM, die ab Mitte der 1980er Jahre in den größeren Modellen serienmäßig mit Turboladern ausgestattet waren. Die Kraftübertragung erfolgte über robuste ZF-Getriebe, die im Laufe der Produktion von 12/5-Gängen bis hin zu hochmodernen 20/9-Gang-Vollsynchrongetrieben weiterentwickelt wurden. Diese ermöglichten bereits frühzeitig eine Höchstgeschwindigkeit von 40 km/h. Mit der Einführung der LSA-Ausführung (Luxus-Standard-Allrad) wurde zudem die Kabine grundlegend modernisiert; die gummigelagerte Sicherheitskabine bot eine für die damalige Zeit exzellente Schalldämmung und hohen Bedienkomfort. Ein technologischer Exot der Serie war der Favorit 626 LS, der mit über 250 PS und einer speziellen Transaxle-Bauweise für schwerste Bodenbearbeitung konzipiert wurde. Aufgrund ihrer massiven Blockbauweise und der weitgehenden Abwesenheit störanfälliger Elektronik gelten die Traktoren der Favorit 600 Serie bis heute als gesuchte Klassiker, die oft noch im harten Praxiseinsatz zu finden sind. {| class="wikitable" | '''Favorit 600 S/SA''' (1972–1976) || '''Favorit 600 SL''' (1976–1979) || '''Favorit 600 LS''' (1978-1984) || '''Favorit 600 LSA''' (1984-1993) |- | valign="top" | <!-- 600 S/SA --> * {{:Traktorenlexikon: Create|Favorit 610 S}} (FW 181/01) 63 kW (85 PS) * {{:Traktorenlexikon: Create|Favorit 610 SA}} (FWA 181/21) 63 kW (85 PS) * {{:Traktorenlexikon: Create|Favorit 611 S}} (FW 182/01) 77 kW (105 PS) * {{:Traktorenlexikon: Create|Favorit 611 SA}} (FWA 182/21) 77 kW (105 PS) * {{:Traktorenlexikon: Create|Favorit 612 S}} ''eigentlich SA?'' 88 kW (120 PS) * {{:Traktorenlexikon: Create|Favorit 612 SA}} (FWA 184/21) 88 kW (120 PS) * {{:Traktorenlexikon: Create|Favorit 614 SA}} (FWA 184/24) 99 kW (135 PS) | valign="top" | <!-- 600 SL --> * {{:Traktorenlexikon: Create|Favorit 610 SL}} 66 kW (90 PS) * {{:Traktorenlexikon: Create|Favorit 611 SL}} 77 kW (105 PS) * {{:Traktorenlexikon: Create|Favorit 612 SL}} 88 kW (120 PS) * {{:Traktorenlexikon: Create|Favorit 614 SL}} 99 kW (135 PS) * {{:Traktorenlexikon: Create|Favorit 615 SL}} 110 kW (150 PS) | valign="top" | <!-- 600 LS --> * {{:Traktorenlexikon: Create|Favorit 600 LS}} (FW(A) 280) 63 kW (85 PS) * {{:Traktorenlexikon: Create|Favorit 610 LS}} (FW(A) 281) 70 kW (95 PS) * {{:Traktorenlexikon: Create|Favorit 611 LS}} (FWA 282)(105PS) * {{:Traktorenlexikon: Create|Favorit 612 LS}} (FWA 283) * {{:Traktorenlexikon: Create|Favorit 614 LS}} (FWA 284) 99 kW (135 PS) * {{:Traktorenlexikon: Create|Favorit 615 LS}} (FWA 285) | valign="top" | <!-- 600 LSA --> * {{:Traktorenlexikon: Create|Favorit 611 LSA}} (FWA 382) * {{:Traktorenlexikon: Create|Favorit 612 LSA}} (FWA 383) 88-107 kW (120-145 PS) * {{:Traktorenlexikon: Create|Favorit 614 LSA}} (FWA 384) 99-121 kW (135-165 PS) * {{:Traktorenlexikon: Create|Favorit 615 LSA}} (FWA 385) ---- * {{:Traktorenlexikon: Create|Favorit 620 LS}} (FWA 291) <!-- Prototyp --> 135 kW (185 PS) * {{:Traktorenlexikon: Create|Favorit 622 LS/LSA}} (FWA 291) 155 kW (211 PS) * {{:Traktorenlexikon: Create|Favorit 626 LS/LSA}} (FWA 292) 185 kW (252 PS) |} ==== Favorit 700 Vario ==== [[Datei:Fendt 716 vario.JPG|miniatur|[[Traktorenlexikon: Fendt Favorit 716 Vario|Favorit 716 Vario]]]] Die Fendt Favorit 700 Vario Serie markierte bei ihrer Einführung im Jahr 1998 einen technologischen Wendepunkt, da sie die erste Mittelklasse-Baureihe weltweit war, die mit dem stufenlosen Vario-Getriebe ausgestattet wurde. Sie löste die erfolgreiche Favorit 500 Serie ab und übernahm deren Konzept des vielseitigen Hochleistungstraktors, ergänzt um die revolutionäre Antriebstechnik des größeren Favorit 900. Die Baureihe umfasste Modelle vom 711 Vario mit 85 kW (115 PS) bis zum Topmodell 716 Vario, das eine Maximalleistung von 118 kW (160 PS) erreichte. Technisch setzten die 700er-Modelle auf wassergekühlte 6-Zylinder-Turbomotoren von Deutz mit einem Hubraum von 6,1 Litern. Das Herzstück war jedoch das ML-160-Vario-Getriebe, das ein vollkommen stufenloses Fahren von 0 bis 50 km/h ermöglichte, ohne dass Schaltvorgänge unterbrochen werden mussten. Ein weiteres Novum war das Varioterminal, über welches der Fahrer die Hydraulik, die Zapfwelle und das Getriebemanagement zentral steuern konnte. In Kombination mit dem Vorgewendemanagement „Variotronic“ bot die Serie einen Automatisierungsgrad, der zuvor nur in der absoluten Oberklasse zu finden war. Der Fahrkomfort wurde durch die Kombination aus gefederter Vorderachse und einer pneumatischen Kabinenfederung auf ein neues Niveau gehoben. Dank ihrer kompakten Abmessungen bei gleichzeitig hoher Motorleistung entwickelte sich die 700er Serie schnell zum Standard für Lohnunternehmer und größere landwirtschaftliche Betriebe. Mit der Überarbeitung zur zweiten Generation und dem Wegfall der Bezeichnung „Favorit“ in der Namensgebung der frühen 2000er Jahre wurde die Serie als Fendt 700 Vario fortgeführt. Sie gilt bis heute als eine der meistverkauften und wertstabilsten Baureihen im Fendt-Portfolio. {| class="wikitable" |- | valign="top" | * {{:Traktorenlexikon: Create|Favorit 711 Vario}} (FWA 711) 85 kW (115 PS) * {{:Traktorenlexikon: Create|Favorit 712 Vario}} (FWA 712) 92 kW (125 PS) * {{:Traktorenlexikon: Create|Favorit 714 Vario}} (FWA 714) 103 kW (140 PS) * {{:Traktorenlexikon: Create|Favorit 716 Vario}} (FWA 716) 118 kW (160 PS) |} ==== Favorit 800 ==== [[File:Fendt Favorit 816.jpg|thumb|Fendt Favorit 816]] Die Fendt Favorit 800 Serie wurde 1993 als neue Hochleistungsklasse oberhalb der 600er-Serie eingeführt und markierte für Fendt den Übergang zum modernen Großtraktoren-Design. Die Baureihe umfasste die Modelle 816, 818, 822 und 824 mit einem Leistungsspektrum von 118 kW (160 PS) bis 176 kW (240 PS). Ziel der Entwicklung war es, die enorme Zugkraft der schweren Sechszylindermodelle mit modernstem Bedienkomfort und einer höheren Transportgeschwindigkeit zu kombinieren. Technisch basierte die Serie auf wassergekühlten 6-Zylinder-Turbomotoren von MAN mit 6,9 Litern Hubraum und Ladeluftkühlung. Als Kraftübertragung diente das ZF-Turboshift-Getriebe, das über sechs Gänge und vier Lastschaltstufen verfügte, was in Kombination mit der Kriechgruppe insgesamt 44 Vorwärts- und 44 Rückwärtsgänge ergab. Ein herausragendes Merkmal war die serienmäßige Höchstgeschwindigkeit von 50 km/h sowie die für diese Zeit revolutionäre gefederte Vorderachse, die in Verbindung mit der bewährten Turbomatik-Flüssigkeitskupplung für einen im Großtraktorensegment unerreichten Fahrkomfort sorgte. Die Kabine der 800er-Serie bot durch ihre großzügige Verglasung und die ergonomische Anordnung der Bedienelemente auf der Seitenkonsole einen modernen Arbeitsplatz. Mit der Einführung der Baureihe wurden auch Funktionen wie die elektrohydraulische Hubwerksregelung (EHR) und ein umfassendes Überwachungssystem zum Standard. Aufgrund ihrer massiven Bauweise und der hohen Zuverlässigkeit bei schwerster Bodenbearbeitung genießen die Modelle der 800er-Serie (insbesondere der 824) heute einen legendären Ruf und erzielen auf dem Gebrauchtmarkt Spitzenpreise. Ab 1996 wurde die Serie durch die ersten 900 Vario Modelle nach oben ergänzt, blieb aber aufgrund ihres bewährten Lastschaltgetriebes bis zur Produktionseinstellung im Jahr 2003 eine beliebte Wahl für Großbetriebe und Lohnunternehmen. {| class="wikitable" | '''Favorit 800''' (1993–2004) |- | valign="top" | * {{:Traktorenlexikon: Create|Favorit 816}} (FWA 816) 125 kW (170 PS) * {{:Traktorenlexikon: Create|Favorit 818}} (FWA 818) 140 kW (190 PS) * {{:Traktorenlexikon: Create|Favorit 822}} (FWA 822) 154 kW (210 PS) * {{:Traktorenlexikon: Create|Favorit 824}} (FWA 824) 169 kW (230 PS) |} ==== Favorit 900 Vario==== [[File:Fendt Favorit 926 Vario Agritechnica 2025 (DSC04511).jpg|thumb|Fendt Favorit 926 Vario (eckige Haube)]] [[File:Eppingen-Adelshofen - Fendt Favorit 926 Vario - 23.06.2019 15-08-46.jpg|thumb|Fendt Favorit 926 Vario (runde Haube)]] Die Fendt Favorit 900 Vario Serie markierte bei ihrer Vorstellung im Jahr 1996 eine Zäsur in der Landtechnikgeschichte, da sie als weltweit erste Großtraktorenbaureihe mit einem stufenlosen Getriebe ausgestattet war. Mit dem Flaggschiff-Modell 926 Vario setzte Fendt einen neuen Industriestandard für schwere Schlepper, die maximale Zugkraft mit präziser Geschwindigkeitskontrolle kombinierten. Die Baureihe umfasste in der ersten Generation Leistungen von 118 kW (160 PS) bis 191 kW (260 PS) und löste die Position der Serie 800 als technologische Speerspitze ab. Das Herzstück der Serie war das ML-200-Vario-Getriebe, eine leistungsverzweigte Einheit, die es ermöglichte, vollkommen ohne Schaltstufen von 0,02 km/h bis zu einer Höchstgeschwindigkeit von 50 km/h zu fahren. Als Antrieb dienten wassergekühlte 6-Zylinder-Turbomotoren von MAN mit 6,9 Litern Hubraum, die für ihr hohes Drehmoment und ihre Effizienz bekannt waren. Ein wesentliches Merkmal war die Integration des Varioterminals, das erstmals eine zentrale, digitale Steuerung aller Maschinenfunktionen sowie ein automatisiertes Vorgewendemanagement ermöglichte. Zusätzlich zum innovativen Antrieb bot die 900er Serie einen überragenden Fahrkomfort durch die serienmäßige Vorderachsfederung und eine pneumatische Kabinenfederung, die Vibrationen bei schweren Ackerarbeiten und schnellen Straßenfahrten effektiv minimierten. Das Design war geprägt von einer bulligen Erscheinung und der charakteristischen, geräumigen Kabine, die eine exzellente Rundumsicht bot. Durch den Erfolg des stufenlosen Konzepts wurde der Name „Vario“ zum Markenzeichen von Fendt. In den frühen 2000er Jahren entfiel der Namenszusatz „Favorit“, und die Serie wurde als Fendt 900 Vario kontinuierlich weiterentwickelt (bis hin zu Modellen mit über 400 PS), wobei sie bis heute die technologische Referenz im Bereich der Standard-Großtraktoren darstellt. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|Favorit 916 Vario}} 132 kW (180 PS) * {{:Traktorenlexikon: Create|Favorit 920 Vario}} 162 kW (220 PS) * {{:Traktorenlexikon: Create|Favorit 924 Vario}} 176 kW (240 PS) * {{:Traktorenlexikon: Create|Favorit 926 Vario}} 191 kW (260 PS) |} === Xylon === [[File:Xylon 520.JPG|thumb|Fendt Xylon 520]] Der Fendt Xylon wurde 1994 als innovatives Systemfahrzeug eingeführt und stellte eine radikale Weiterentwicklung des klassischen Geräteträger-Konzepts dar. Er wurde entwickelt, um als universelle Arbeitsmaschine in der Landwirtschaft, im Kommunaldienst und in der Forstwirtschaft zu fungieren. Im Gegensatz zum herkömmlichen Standardtraktor zeichnete sich der Xylon durch seine Bauweise mit vier Anbauräumen (Front, Heck, Aufbauraum über der Hinterachse und Frontladeranbau) sowie eine mittig angeordnete, großzügige Kabine aus, die eine hervorragende Rundumsicht auf alle Arbeitsbereiche bot. Technisch basierte der Xylon auf der bewährten Antriebstechnik der Favorit 500 Serie. Er war mit wassergekühlten 4-Zylinder-Turbomotoren von MAN ausgestattet, die Leistungen von 88 kW (120 PS) beim Modell 520 bis zu 108 kW (147 PS) beim Modell 524 erbrachten. Als Getriebe diente das ZF-Turboshift-Getriebe mit 44 Vorwärts- und 44 Rückwärtsgängen inklusive der Turbomatik-Flüssigkeitskupplung, was dem Fahrzeug eine Höchstgeschwindigkeit von 50 km/h ermöglichte. Ein besonderes technisches Merkmal war die gefederte Vorderachse mit Niveauregulierung, die zusammen mit der zentralen Kabinenposition für einen überdurchschnittlichen Fahrkomfort und eine optimale Gewichtsverteilung sorgte. Trotz seiner enormen Vielseitigkeit und der Fähigkeit, komplexe Gerätekombinationen in einem Arbeitsgang zu führen, blieb der Xylon ein Nischenfahrzeug. Die aufwendige Konstruktion und die damit verbundenen hohen Anschaffungskosten sowie der Trend zu noch leistungsstärkeren Standardtraktoren mit stufenlosem Getriebe führten dazu, dass die Produktion im Jahr 2004 eingestellt wurde. Dennoch genießt der Xylon heute aufgrund seines einzigartigen Konzepts und seiner Robustheit einen besonderen Status und ist insbesondere im kommunalen Winterdienst und bei spezialisierten Lohnunternehmen weiterhin im Einsatz. {| class="wikitable" | '''Fendt Xylon''' |- | valign="top" | * {{:Traktorenlexikon: Create|Xylon 320}} 88 kW (120 PS) * {{:Traktorenlexikon: Create|Xylon 520}} 81 kW (110 PS) * {{:Traktorenlexikon: Create|Xylon 522}} 92 kW (125 PS) * {{:Traktorenlexikon: Create|Xylon 524}} 108 kW (147 PS) |} === Vario === Die Fendt Vario Modelle repräsentieren seit ihrer Markteinführung im Jahr 1995 die technologische Speerspitze der Marke und markieren den vollständigen Übergang zum stufenlosen Antrieb in allen Leistungsklassen. Das Herzstück dieser Traktoren ist das Vario-Getriebe, ein leistungsverzweigtes, hydrostatisch-mechanisches Getriebe, das es ermöglicht, jede Geschwindigkeit von 20 Metern pro Stunde bis zu 60 km/h ohne Schaltstufen und ohne Zugkraftunterbrechung zu erreichen. Dieses System revolutionierte die Branche, da es die Motorleistung stets im optimalen Bereich hielt und den Bedienkomfort durch die Steuerung via Fahrhebel oder Fahrpedal radikal vereinfachte. Technisch zeichnen sich die Vario-Modelle durch eine tiefgreifende Integration von Elektronik und Mechanik aus, die im Varioterminal zusammenläuft. Dieses zentrale Steuerungssystem ermöglicht die präzise Kontrolle über das Getriebemanagement (TMS), die Hydraulik, die Zapfwellen und moderne Spurführungssysteme. Über die Jahrzehnte wurde das Vario-Konzept auf das gesamte Portfolio ausgeweitet – vom kompakten 200 Vario für Sonderkulturen über die Allrounder der 300er, 500er und 700er Serien bis hin zu den Großtraktoren der 900er und 1000er Vario Serien mit bis zu 517 PS. Die Vario-Technologie hat Fendt weltweit als Innovationsführer etabliert und sorgt durch die hohe Effizienz und den enormen Wiederverkaufswert für eine Sonderstellung auf dem Landmaschinenmarkt. Bei allen Modellen wird TMS nicht in der Bezeichnung aufgeführt. ==== 200 Vario V/F/P ==== [[File:Fendt 211 V Vario Agritechnica 2025 (DSC04457).jpg|thumb|Fendt 211 V Vario]] Die Fendt 200 V/F/P Vario Serie stellt die spezialisierte Antwort auf die extremen Anforderungen im Wein-, Obst- und Hopfenbau dar und brachte 2009 als erste Spezialtraktoren-Baureihe weltweit das stufenlose Getriebe in dieses Segment. Die drei Buchstaben definieren dabei die Einsatzschwerpunkte: V (Vineyard) steht für den klassischen, schmalen Weinbauschlepper (ab 1,07 m Außenbreite), F für die etwas breitere Ausführung mit komfortablerer Kabine (ab 1,32 m) und P (Plantage) für den Spezialtraktor mit breiteren Achsen und höheren Hubkräften (ab 1,59 m) für den Obst- oder Hopfenanbau. Das technische Kernstück ist das für diese Größe optimierte Vario-Getriebe ML 70, das ein völlig ruckfreies Arbeiten und exaktes Manövrieren von 0,02 bis 40 km/h ermöglicht – ein entscheidender Vorteil bei Pflegearbeiten in engen Sonderkulturen. Als Antrieb dienen kompakte 3-Zylinder-Motoren von AGCO Power mit 3,3 Litern Hubraum, die in der aktuellen Generation (Gen3) durch das Mehrleistungskonzept DynamicPerformance beim Spitzenmodell 211 Vario bis zu 91 kW (124 PS) freisetzen. In der neuesten Ausbaustufe wurde die Baureihe durch das FendtONE-Bedienkonzept grundlegend digitalisiert. Die Kabine ist nun als 4-Pfosten-Einheit konzipiert, verzichtet auf einen störenden Getriebetunnel und bietet eine optionale Kat.-4-Filteranlage zum Schutz des Fahrers bei Pflanzenschutzarbeiten. Mit Features wie der gefederten Vorderachse inklusive Wankabstützung und einer leistungsstarken Load-Sensing-Hydraulik (bis zu 119 l/min) kombinieren diese kompakten Maschinen die High-Tech-Standards der Großtraktoren mit der nötigen Wendigkeit für spezialisierte Kulturen. Durch die drei Ausstattungslinien Power, Profi und Profi+ lässt sich die Serie zudem präzise auf die Bedürfnisse von Sonderkulturbetrieben oder kommunalen Einsätzen zuschneiden. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|207 V/F}} 51 kW (70 PS) * {{:Traktorenlexikon: Create|208 V/F}} 59 kW (80 PS) * {{:Traktorenlexikon: Create|209 V/F/P}} 67 kW (90 PS) * {{:Traktorenlexikon: Create|210 V/F/P}} 73 kW (100 PS) * {{:Traktorenlexikon: Create|211 V/F/P}} 81 kW (110 PS) |} ==== 200 Vario ==== [[File:Fendt 211 Vario Agritechnica 2025 (DSC04422).jpg|thumb|Fendt 211 Vario]] Die Fendt 200 Vario Serie (Standardausführung) wurde 2009 eingeführt und brachte das stufenlose Getriebe erstmals in das Segment der kompakten Standardschlepper. Sie löste die klassische Farmer 200 Serie ab und deckt den Leistungsbereich von etwa 57 kW (77 PS) bis 82 kW (111 PS) ab. Während die V/F/P-Varianten für Sonderkulturen optimiert sind, ist der Standard-200er als wendiger Allrounder für Grünlandbetriebe, die Hofwirtschaft und als Pflegeschlepper konzipiert. Technisch basiert die Serie auf einem kompakten 3-Zylinder-Motor von AGCO Power, der durch seine Spritzigkeit und die platzsparende Bauweise überzeugt. Das Vario-Getriebe ML 70 ermöglicht ein stufenloses Fahren von 20 Metern pro Stunde bis zu einer Höchstgeschwindigkeit von 40 km/h bei abgesenkter Motordrehzahl. Ein wesentlicher Vorteil gegenüber der vorangegangenen Generation ist der ebene Kabinenboden, der durch den Wegfall des Getriebetunnels realisiert wurde und in dieser Größenklasse eine herausragende Ergonomie und Bewegungsfreiheit bietet. In der aktuellen Generation wurde auch dieser Baureihe das FendtONE-Bedienkonzept spendiert, welches ein digitales Dashboard und eine neue Seitenkonsole mit Multifunktions-Joystick umfasst. Mit Ausstattungsmerkmalen wie der Vorderachsfederung, einer leistungsstarken Hydraulik und der optionalen Rückfahreinrichtung vereint der 200 Vario die Technologie der Großtraktoren mit der Kompaktheit eines mittleren Schleppers. Aufgrund seines geringen Eigengewichts und der hohen Wendigkeit ist er besonders bei Betrieben in Bergregionen sowie für Frontladerarbeiten hochgradig geschätzt. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|207 Vario}} 51 kW (70 PS) * {{:Traktorenlexikon: Create|208 Vario}} 59 kW (80 PS) * {{:Traktorenlexikon: Create|209 Vario}} 67 kW (90 PS) * {{:Traktorenlexikon: Create|210 Vario}} 73 kW (100 PS) * {{:Traktorenlexikon: Create|211 Vario}} 81 kW (110 PS) |} ==== 300 Vario ==== [[Datei:Fendt 312 Vario TMS Traktor.jpg|mini|Fendt 312 Vario TMS]] [[File:Fendt 313 Vario en Lövsta 02.jpg|thumb|Fendt 313 Vario]] Die Fendt 300 Vario Serie markierte im Jahr 2006 einen historischen Wendepunkt, indem sie die über Jahrzehnte erfolgreiche Farmer-300-Baureihe endgültig in das Zeitalter des stufenlosen Antriebs überführte. Als kompakter Standardschlepper deckt er heute ein Leistungsspektrum von etwa 100 bis 152 PS ab und gilt als eines der meistverkauften Modelle im Fendt-Portfolio. Sein Erfolg basiert auf der Kombination aus einem geringen Eigengewicht von rund 5 Tonnen und einer hohen Nutzlast, was ihn zum idealen Allrounder für Grünlandbetriebe, den Ackerbau und professionelle Frontladerarbeiten macht. Technisch wird die Serie in der aktuellen Generation von einem 4,4-Liter-AGCO-Power-Motor angetrieben, der durch das innovative Mehrleistungskonzept DynamicPerformance (DP) bedarfsabhängig zusätzliche Kraft freisetzt, sobald Nebenverbraucher wie die Klimaanlage oder die Zapfwelle Energie beanspruchen. Das bewährte Vario-Getriebe ML 75 sorgt für eine stufenlose Beschleunigung von 20 Metern pro Stunde bis zur Höchstgeschwindigkeit von 40 km/h bei reduzierter Motordrehzahl, was Kraftstoff spart und das Geräuschniveau in der Kabine senkt. Mit der Einführung der vierten Generation (Gen4) hielt zudem das FendtONE-Bedienkonzept Einzug, welches die Steuerung im Traktor über ein digitales Dashboard und eine moderne Multifunktionsarmlehne mit dem Büroarbeitsplatz vernetzt. Die Baureihe ist modular in den Ausstattungslinien Power, Profi und Profi+ aufgebaut, wodurch sie sich präzise an unterschiedliche Anforderungen anpassen lässt – von der einfachen Basismaschine für den Hofbetrieb bis hin zur High-Tech-Variante mit Spurführung und ISOBUS-Steuerung für den Präzisionsackerbau. Ein besonderes Merkmal ist die hohe Bodenfreiheit von bis zu 500 mm und die integrierte Vorderachsfederung, die zusammen mit der VisioPlus-Kabine für eine überlegene Sicht und hohen Fahrkomfort sorgt. Durch ihre enorme Wertstabilität und Vielseitigkeit setzt die 300er-Vario-Serie die Tradition des „Farmer“ als universelles Arbeitspferd der modernen Landwirtschaft fort. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|309 Vario}} 70 kW (95 PS) * {{:Traktorenlexikon: Create|310 Vario}} 77 kW (105 PS) * {{:Traktorenlexikon: Create|311 Vario}} 84 kW (115 PS) * {{:Traktorenlexikon: Create|312 Vario}} 92 kW (125 PS) * {{:Traktorenlexikon: Create|313 Vario}} 99 kW (135 PS) * {{:Traktorenlexikon: Create|314 Vario}} 104KW (142 PS) |} ==== 400 Vario ==== [[File:Fendt 415 Vario, Claas Liner.jpg|thumb|Fendt 415 Vario]] Die Fendt 400 Vario Serie markierte bei ihrer Einführung im Jahr 1999 einen historischen Wendepunkt in der Modellpolitik von Fendt, da sie die weltweit erste Baureihe war, die das stufenlose Getriebe in das Segment der kompakten Vierzylinder-Traktoren brachte. Sie bildete die Brücke zwischen der kompakten 300er-Klasse und der größeren 700er-Serie und deckte ein Leistungsspektrum von etwa 95 PS bis 125 PS ab. Mit ihrem kurzen Radstand und der hohen Leistungsdichte war sie primär als wendiger Allrounder für Frontladerarbeiten, den Grünlandeinsatz und schwere Pflegearbeiten konzipiert. Ein entscheidender Einschnitt erfolgte mit der umfassenden Modellpflege im Jahr 2006: In diesem Zuge wurde die traditionsreiche Bezeichnung „Farmer“ offiziell gestrichen. Während die erste Generation noch als „Farmer 400 Vario“ vermarktet wurde, firmierte die überarbeitete Baureihe fortan schlicht als Fendt 400 Vario. Dieser Namenswechsel symbolisierte den endgültigen Übergang von der klassischen Mechanik-Ära hin zur reinen High-Tech-Marke, bei der das stufenlose Vario-Getriebe nun zum alleinigen Standard geworden war. Technisch basierte die Serie auf spritzigen 4-Zylinder-Motoren von Deutz, die in Kombination mit dem Vario-Getriebe ML 75 ein völlig ruckfreies Arbeiten von 0,02 bis 50 km/h ermöglichten. Trotz des Wegfalls des Namenszusatzes blieb der 400er ein würdiger Nachfolger der Farmer-Tradition, bot jedoch High-Tech-Komponenten der Oberklasse, wie das Varioterminal und die elektronische Hubwerksregelung (EHR). Die Leistung der Topmodelle, wie dem 415 Vario, stieg in der Ära ohne den „Farmer“-Schriftzug auf bis zu 155 PS an. Heute gilt diese Baureihe als einer der wertstabilsten Klassiker auf dem Gebrauchtmarkt, da sie die Kompaktheit der alten Farmer-Serie mit der modernen Leistungsfähigkeit der Vario-Technik vereint. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|411 Vario}} 85 kW (115 PS) * {{:Traktorenlexikon: Create|412 Vario}} 92 kW (125 PS) * {{:Traktorenlexikon: Create|413 Vario}} 99 kW (135 PS) * {{:Traktorenlexikon: Create|414 Vario}} 107 kW (145 PS) * {{:Traktorenlexikon: Create|415 Vario}} 114 kW (155 PS) |} ==== 500 Vario ==== [[File:Fendt 516 Vario Profi+ Agritechnica 2025 (DSC04534).jpg|thumb|Fendt 516 Vario]] Die Fendt 500 Vario Serie wurde im Jahr 2012 als direkter Nachfolger der erfolgreichen 400er-Baureihe eingeführt und vollendete den strategischen Verzicht auf den Namen „Farmer“. Während die Vorgängermodelle noch die historische Brücke zur Mittelklasse schlugen, wurde die 500er-Serie konsequent als kompakter Hochleistungsschlepper positioniert, der die Technik der Großtraktoren in eine handlichere Bauform transferierte. Sie deckt ein Leistungsspektrum von etwa 125 PS bis 165 PS ab und besetzt damit die wichtige Lücke zwischen dem 300 Vario und dem 6-Zylinder-Modell 700 Vario. Technisch markierte der 500 Vario einen deutlichen Sprung, da er erstmals die VisioPlus-Kabine der größeren Baureihen in diese Klasse brachte. Mit der tief heruntergezogenen Frontscheibe und dem modernen Bedienkonzept bot er eine Ergonomie, die weit über das Maß früherer Kompaktschlepper hinausging. Angetrieben wird die Serie von einem 4,04-Liter-Vierzylindermotor von Deutz, der in Kombination mit dem stufenlosen Vario-Getriebe ML 90 eine Höchstgeschwindigkeit von 50 km/h bei reduzierter Motordrehzahl ermöglicht. Diese Kombination macht den 500er zu einer extrem vielseitigen Maschine, die sowohl im schweren Frontladereinsatz als auch bei Transportarbeiten und im Ackerbau überzeugt. Mit der Einführung der neuesten Generation wurde auch hier das FendtONE-System implementiert, welches die Vernetzung zwischen der Maschine und dem Büroarbeitsplatz ins Zentrum rückt. Trotz des Fehlens des Namens „Farmer“ führt der 500 Vario dessen Erbe als universelles Arbeitspferd fort, jedoch auf einem technologischen Niveau, das Funktionen wie vollautomatisches Lenken, ISOBUS-Gerätesteuerung und präzise Spurführung zum Standard macht. Durch sein günstiges Leistungsgewicht und die kompakten Abmessungen bleibt er die erste Wahl für Betriebe, die maximale Schlagkraft bei hoher Wendigkeit fordern. {| class="wikitable" |- | valign="top" | * {{:Traktorenlexikon: Create|512 Vario}} 92 kW (125 PS) * {{:Traktorenlexikon: Create|513 Vario}} 99 kW (135 PS) * {{:Traktorenlexikon: Create|514 Vario}} 107 kW (145 PS) * {{:Traktorenlexikon: Create|516 Vario}} 121 kW (165 PS) |} {{clear}} ==== 600 Vario ==== [[File:Fendt 620 Vario Profi+ Agritechnica 2025 (DSC06624).jpg|thumb|Fendt 620 Vario]] Die Fendt 600 Vario Serie wurde im Jahr 2023 als komplett neu entwickelte Baureihe vorgestellt und besetzt das Segment der leistungsstarken Vierzylinder-Traktoren im Bereich von 150 bis 224 PS. Mit dieser Serie belebte Fendt die legendäre „600er“-Nummerierung wieder, die historisch für die schweren Favorit-Modelle stand, verzichtete jedoch konsequent auf jegliche Zusatzbezeichnungen wie „Farmer“ oder „Favorit“. Die Serie schließt die Lücke zwischen dem kompakten 500 Vario und dem großen 6-Zylinder-Modell 700 Vario und richtet sich an Betriebe, die maximale Leistung bei hoher Wendigkeit und geringem Bodendruck suchen. Das technische Herzstück ist der neu entwickelte AGCO Power Core50 Motor mit 5 Litern Hubraum, der speziell für das Niedrigdrehzahlkonzept Fendt iD konstruiert wurde. Damit erreicht der Traktor sein maximales Drehmoment bereits bei 1.350 U/min, was ihn besonders kraftstoffeffizient macht. Eine Besonderheit in dieser Leistungsklasse ist das Spitzenmodell 620 Vario, das durch das Mehrleistungskonzept DynamicPerformance (DP) bis zu 224 PS freisetzt. Kraftübertragung und Antrieb werden über den neuen VarioDrive-Fahrantrieb geregelt, der die Vorder- und Hinterachse unabhängig voneinander ansteuert, wodurch der „Pull-in-turn“-Effekt für einen extrem kleinen Wendekreis sorgt. Die 600er-Serie übernimmt die großräumige VisioPlus-Kabine und ist vollständig in das digitale Ökosystem FendtONE integriert. Dies ermöglicht eine lückenlose Vernetzung von der Maschine bis in das Büro und unterstützt Funktionen für den Präzisionsackerbau. Trotz des Verzichts auf die historischen Namen zeigt die Serie durch ihre Robustheit und das universelle Einsatzspektrum – vom schweren Zugbetrieb über Transportaufgaben bis hin zu Zapfwellenarbeiten –, dass sie die Tradition der vielseitigen Allrounder in die moderne, digitale Landwirtschaft überführt. Mit einem zulässigen Gesamtgewicht von bis zu 13,5 Tonnen bietet der 600 Vario zudem eine Nutzlast, die ihn für professionelle Lohnunternehmen und Großbetriebe prädestiniert. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|614 Vario}} 110 kW (149 PS) * {{:Traktorenlexikon: Create|616 Vario}} 124 kW (169 PS) * {{:Traktorenlexikon: Create|618 Vario}} 139 kW (189 PS) * {{:Traktorenlexikon: Create|620 Vario}} 154 kW (209 PS) |} ==== 700 Vario ==== [[File:Fendt 728 Vario Gen7 Agritechnica 2023 (DSC05099).jpg|thumb|Fendt 728 Vario Gen7]] Die Fendt 700 Vario Serie wurde im Jahr 1998 eingeführt und markierte den endgültigen Verzicht auf die traditionsreichen Namen „Farmer“ und „Favorit“ im mittleren bis oberen Leistungssegment. Als Nachfolger der Favorit 500er-Serie konzipiert, wurde sie schnell zum Herzstück des Fendt-Programms und gilt heute als der meistverkaufte Universaltraktor in Deutschland. Mit der rein numerischen Bezeichnung signalisierte Fendt den Aufbruch in eine neue Ära, in der die Vario-Technik zum Standard für professionelle Allround-Maschinen wurde. Technisch setzte der 700 Vario Maßstäbe durch die Kombination aus kompakten Abmessungen und leistungsstarken 6-Zylinder-Motoren von Deutz. Das Vario-Getriebe ML 140 (beziehungsweise ML 180 bei den größeren Modellen) ermöglichte ein stufenloses Fahren bis zu 50 km/h und später sogar 60 km/h. Die Serie deckte ursprünglich einen Bereich von 115 bis 160 PS ab, hat sich jedoch über sieben Generationen hinweg kontinuierlich weiterentwickelt. In der aktuellen Generation Gen7 erreicht das Spitzenmodell 728 Vario dank des AGCO Power Core75 Motors und des Mehrleistungskonzepts DynamicPerformance (DP) eine Maximalleistung von bis zu 303 PS. Ein wesentliches Merkmal des 700 Vario ist seine enorme Vielseitigkeit: Er ist schwer genug für die Bodenbearbeitung, aber gleichzeitig wendig und leicht genug für den Frontladereinsatz und die Grünlandarbeit. Mit der Einführung der VisioPlus-Kabine und dem digitalen Bedienkonzept FendtONE wurde die Serie zudem zum Vorreiter in Sachen Smart Farming und Ergonomie. Trotz des Fehlens der Bezeichnung „Favorit“ führt der 700 Vario dessen Erbe als technologisch führender Mehrzweckschlepper fort und ist heute weltweit der Standard für mittlere bis große landwirtschaftliche Betriebe sowie Lohnunternehmen. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|712 Vario}} 96 kW (130 PS) * {{:Traktorenlexikon: Create|714 Vario}} 107 kW (145 PS) * {{:Traktorenlexikon: Create|716 Vario}} 121 kW (165 PS) * {{:Traktorenlexikon: Create|718 Vario}} 132 kW (180 PS) * {{:Traktorenlexikon: Create|720 Vario}} 147 kW (200 PS) * {{:Traktorenlexikon: Create|722 Vario}} 162 kW (220 PS) * {{:Traktorenlexikon: Create|724 Vario}} 176 kW (240 PS) |- | ab 2022 (Gen.7 mit AGCO Motoren) |- | valign="top" | * {{:Traktorenlexikon: Create|720 Vario}} 149 kW (203 PS) * {{:Traktorenlexikon: Create|722 Vario}} 164 kW (223 PS) * {{:Traktorenlexikon: Create|724 Vario}} 179 kW (243 PS) * {{:Traktorenlexikon: Create|726 Vario}} 193 kW (262 PS) * {{:Traktorenlexikon: Create|728 Vario}} 208 kW (283 PS) |} Die Links gehen noch auf die "alten" 700er. ==== 800 Vario ==== [[Datei:Fendt 820 Vario TMS Traktor mit Einachskippanhänger (2024).jpg|mini|Fendt 820 Vario TMS]] [[File:Fendt 832 Vario Gen5 Profi+ Agritechnica 2025 (DSC04437).jpg|thumb|Fendt 832 Vario Gen5]] Die Fendt 800 Vario Serie markierte im Jahr 2003 eine Zäsur in der Modellstrategie, da mit ihrer Einführung die traditionsreiche Bezeichnung „Favorit“ endgültig abgelegt wurde. Fortan firmierte die Baureihe als rein numerische Serie, was den strategischen Fokus vollständig auf das technologische Alleinstellungsmerkmal, das stufenlose Vario-Getriebe, rückte. Dieser Namenswechsel signalisierte den Aufbruch in eine Ära, in der High-Tech-Komponenten nicht mehr als Zusatzoption, sondern als integraler Standard der Marke definiert wurden. Technisch präsentierte sich die Serie ab 2003 zunächst mit den Modellen 815, 817 und 818 Vario TMS, die eine Brücke zwischen der kompakten Mittelklasse und den schweren Großtraktoren schlugen. Ausgestattet mit spritzigen 6-Zylinder-Motoren von Deutz und dem Vario-Getriebe ML 160, boten diese Maschinen Leistungen von bis zu 190 PS in einer für diese Kraftklasse ungewöhnlich kompakten Bauform. Ein zentrales Merkmal war das serienmäßige Tractor Management System (TMS), das die elektronische Vernetzung von Motor und Getriebe übernahm, um stets im wirtschaftlichen Optimum zu arbeiten und den Fahrer massiv zu entlasten. Über die Jahre entwickelte sich die 800 Vario Serie zu einem Synonym für hocheffiziente Transport- und Ackerleistung. In späteren Ausbaustufen, wie der 800 Vario SCR ab 2010, stieg die Leistung der Topmodelle auf bis zu 287 PS an. Trotz dieser enormen Kraftentfaltung blieb die Serie ihrem Ruf als wendiger Großtraktor treu, der durch Features wie die 60 km/h Höchstgeschwindigkeit, die leistungsstarke Hydraulik und das moderne Varioterminal bestach. Heute gelten insbesondere die frühen Modelle ab 2003 aufgrund ihrer Robustheit und der wegweisenden Bedienlogik als gesuchte Klassiker, die den Grundstein für das heutige, rein numerische Produktsortiment von Fendt legten. {| class="wikitable" | valign="top" | * {{:Traktorenlexikon: Create|815 Vario}} 110 kW (150 PS) * {{:Traktorenlexikon: Create|817 Vario}} 121 kW (165 PS) * {{:Traktorenlexikon: Create|818 Vario}} 136 kW (185 PS) * {{:Traktorenlexikon: Create|820 Vario}} 152 kW (205 PS) {{:Traktorenlexikon: Create|820 Vario greentec}} 151 kW (205 PS) | valign="top" | {{:Traktorenlexikon: Create|819 Vario}} 147 kW (200 PS) * {{:Traktorenlexikon: Create|822 Vario}} 162 kW (220 PS) * {{:Traktorenlexikon: Create|824 Vario}} 176 kW (240 PS) * {{:Traktorenlexikon: Create|826 Vario}} 191 kW (260 PS) * {{:Traktorenlexikon: Create|828 Vario}} 206 kW (280 PS) | valign="top" | {{:Traktorenlexikon: Create|826 Vario}} 208 kW (283 PS) * {{:Traktorenlexikon: Create|829 Vario}} 230 kW (313 PS) * {{:Traktorenlexikon: Create|832 Vario}} 252 kW (343 PS) |} ==== 900 Vario ==== [[Datei:Fendt 936.jpg|thumb|[[Traktorenlexikon: Fendt 936 Vario|Fendt 936 Vario „Black Beauty“]]]] [[File:Dowerin Field Days, 2021 (50).jpg|thumb|Fendt 939 Vario]] Die Fendt 900 Vario Serie markierte im Jahr 2005 einen entscheidenden Wendepunkt in der Geschichte der Großtraktoren, als mit der Einführung der vierten Generation (Modelle 922 bis 936 Vario). Der Fendt 936 Vario wird als „Black Beauty“ auf der AGRITECHNICA 2005 zum absoluten Hingucker. Während der erste Vario der Welt im Jahr 1995 noch als „Favorit 926 Vario“ firmierte. Ab der dritten Generation (dem Fendt Vario 930 TMS aus dem Jahr 2002) Dieser Verzicht auf den Namen „Favorit“ unterstrich den Anspruch, das Vario-Getriebe nicht mehr als technologische Besonderheit, sondern als das fundamentale Markenzeichen aller Fendt-Großschlepper zu etablieren. Technisch setzte die 900er-Serie ab 2005 neue Maßstäbe im schweren Ackerbau und im schnellen Transport. Ausgestattet mit 6-Zylinder-Deutz-Motoren und einem Hubraum von 7,1 Litern, deckten die Maschinen einen Leistungsbereich von 220 PS bis hin zum damaligen Spitzenmodell 936 Vario mit 360 PS ab. Das Herzstück war das Vario-Getriebe ML 260, das eine Höchstgeschwindigkeit von 60 km/h ermöglichte. Ein technologischer Meilenstein war die Einführung der unabhängigen Vorderachsfederung mit Niveauregulierung sowie die Integration des Varioterminals, das die Kontrolle über Getriebe, Hydraulik und ISOBUS-Geräte zentralisierte. In den darauffolgenden Generationen wurde die Serie konsequent weiterentwickelt und erreichte mit der aktuellen Baureihe (Gen7) Leistungen von bis zu 415 PS. Trotz des Fehlens der Bezeichnung „Favorit“ blieb die 900er-Serie das globale Flaggschiff für den schweren Zugbetrieb, zeichnete sich jedoch durch das innovative Niedrigdrehzahlkonzept Fendt iD und das neue VarioDrive-Antriebssystem aus, das die Vorder- und Hinterachse unabhängig voneinander ansteuert. Heute gilt der 900 Vario als einer der erfolgreichsten Großtraktoren weltweit, der die Robustheit der alten Favorit-Modelle mit modernster digitaler Vernetzung (FendtONE) und maximaler Kraftstoffeffizienz kombiniert. {| class="wikitable" | valign="top" | {{:Traktorenlexikon: Create|916 Vario}} 146 kW (198 PS) {{:Traktorenlexikon: Create|920 Vario}} 154 kW (210 PS) {{:Traktorenlexikon: Create|922 Vario}} 162 kW (220 PS) {{:Traktorenlexikon: Create|924 Vario}} 176 kW (240 PS) {{:Traktorenlexikon: Create|926 Vario}} 199 kW (270 PS) | valign="top" | {{:Traktorenlexikon: Create|927 Vario}} 199 kW (270 PS) {{:Traktorenlexikon: Create|930 Vario}} 220 kW (300 PS) {{:Traktorenlexikon: Create|933 Vario}} 242 kW (330 PS) {{:Traktorenlexikon: Create|936 Vario}} 265 kW (360 PS) {{:Traktorenlexikon: Create|939 Vario}} 287 kW (390 PS) | valign="top" | {{:Traktorenlexikon: Create|942 Vario}} 305 kW (415 PS) |} ==== 1000 Vario ==== [[File:Fendt 1052 Vario Gen4 Profi+ Agritechnica 2025 (DSC01013).jpg|thumb|Fendt 1052 Vario Gen4]] Die Fendt 1000 Vario Serie wurde im Jahr 2014 als vollkommen neue Leistungsklasse vorgestellt und 2015 im Markt eingeführt. Anders als die kleineren Baureihen besaß diese Serie von Beginn an keine historische Zusatzbezeichnung wie „Farmer“ oder „Favorit“, sondern wurde direkt in das rein numerische System eingeordnet. Sie begründete das Segment der „Großstandardtraktoren“: Maschinen, welche die enorme Zugkraft von Knicklenkern oder Raupenschleppern bieten, aber gleichzeitig die Wendigkeit und Straßentauglichkeit eines Standardtraktors beibehalten. Technisch stellt der 1000er Vario das Flaggschiff der Marke dar und deckt mit vier Modellen (1038 bis 1050) einen Leistungsbereich von 396 bis 517 PS ab. Als Kraftquelle dient ein 12,4-Liter-Sechszylindermotor von MAN, der speziell auf das Niedrigdrehzahlkonzept Fendt iD optimiert ist. Das maximale Drehmoment von bis zu 2.400 Nm liegt bereits bei 1.100 U/min an, was die Effizienz bei schwerster Bodenbearbeitung massiv steigert. Eine wesentliche Innovation ist das VarioDrive-Antriebssystem, bei dem die Vorder- und Hinterachse über zwei getrennte Hydromotoren unabhängig voneinander angetrieben werden. Dies macht eine starre Allradkupplung überflüssig und reduziert den Wendekreis durch den „Pull-in-turn“-Effekt erheblich. Trotz seiner Größe und einem Leergewicht von rund 14 Tonnen ist der 1000 Vario für eine Höchstgeschwindigkeit von bis zu 60 km/h zugelassen und bleibt durch seine Bauweise voll straßentauglich. In der aktuellen Ausführung ist er vollständig in das FendtONE-Ökosystem integriert, was eine präzise Steuerung komplexer Arbeitsabläufe und eine lückenlose Dokumentation ermöglicht. Der 1000 Vario hat Fendt weltweit in einem Leistungssegment etabliert, das zuvor fast ausschließlich von Spezialmaschinen dominiert wurde, und gilt heute als technologische Referenz für modernste Großtraktoren-Technik. {| class="wikitable" ! ab 2015 |- | valign="top" | {{:Traktorenlexikon: Create|1038 Vario}} (396 PS) {{:Traktorenlexikon: Create|1042 Vario}} (435 PS) {{:Traktorenlexikon: Create|1046 Vario}} (476 PS) {{:Traktorenlexikon: Create|1050 Vario (FWA 530.23)}} (517 PS) |} === Raupentraktoren === [[File:Fendt 1100 MT-2-crop.jpg|thumb|Fendt 1100 Vario MT]] Mit der Integration der Raupentechnologie in das Produktportfolio hat Fendt das Segment der schweren Bodenschonung und maximalen Zugkraft grundlegend transformiert. Die Basis dieser Entwicklung bildet die jahrzehntelange Erfahrung der Marke Challenger, die einst Pionierarbeit bei Gummilaufwerken leistete. Unter der Federführung von Fendt wurden diese Maschinen jedoch technisch völlig neu definiert: Der harte mechanische Antrieb der ursprünglichen Raupenschlepper wurde durch das stufenlose Vario-Getriebe ersetzt, während das moderne Bedienkonzept die Handhabung auf das Niveau der Radtraktoren hob. Heute stehen die Vario MT Modelle für eine hocheffiziente Kraftübertragung bei geringstem Bodendruck, konzipiert für professionelle Betriebe, die unter schwierigsten Bedingungen höchste Schlagkraft fordern. ==== 900 Vario MT ==== [[File:Dowerin Field Days, 2021 (47).jpg|thumb|Fendt 943 Vario MT]] Der Fendt 900 Vario MT markiert den Einstieg in die Welt der stufenlosen Raupenschlepper und deckt den Leistungsbereich von 380 bis 431 PS ab. Als technologischer Nachfolger der Challenger MT700 Serie wurde er gezielt für die Kombination aus hoher Zugleistung und modernstem Fahrkomfort entwickelt. Das Herzstück ist der 9,1-Liter-AGCO-Power-Motor, der perfekt mit dem für Raupen optimierten VarioDrive-Getriebe harmoniert. Ein herausragendes Merkmal ist das innovative Fahrwerkskonzept: Das SmartRide-System kombiniert eine Federung des Hauptrahmens mit den ConstantTouch-Laufrollen, wodurch sich das Laufwerk wie ein Panzerkettenlaufwerk flexibel an Bodenunebenheiten anpasst. Dies sorgt für eine konstante Aufstandsfläche und verhindert das typische „Nicken“ der Maschine. In Verbindung mit der großräumigen Kabine und dem digitalen Bedienkonzept bietet der 900 Vario MT einen Komfort, der in dieser Spezialklasse zuvor nicht verfügbar war. Er ist die ideale Wahl für Betriebe, die schwere Bodenbearbeitung und Saatbettbereitung mit maximaler Bodenschonung verbinden müssen. {| class="wikitable" ! ab 2018 |- | valign="top" | {{:Traktorenlexikon: Create|938 Vario MT}} {{:Traktorenlexikon: Create|940 Vario MT}} {{:Traktorenlexikon: Create|943 Vario MT}} |} ==== 1100 MT ==== [[File:Fendt 1167 Vario MT Agritechnica 2023 (DSC05922).jpg|thumb|Fendt 1167 Vario MT]] Als unangefochtenes Kraftpaket im Sortiment ist der Fendt 1100 Vario MT für die extremsten Anforderungen des Weltmarkts gebaut. In dieser Baureihe, die Leistungen von 511 bis zu 673 PS erreicht, wurde die einstige Challenger-Großraupentechnik vollständig mit der Fendt-Philosophie verschmolzen. Angetrieben von MAN-Sechszylindermotoren mit bis zu 16,2 Litern Hubraum, setzt die Maschine auf das Fendt iD Niedrigdrehzahlkonzept. Dies ermöglicht es, das enorme Drehmoment von bis zu 3.100 Nm bereits in einem sehr niedrigen Drehzahlbereich abzurufen und hocheffizient in Vortrieb umzusetzen. Die technische Überlegenheit zeigt sich besonders bei der Arbeit mit extrem breiten Anbaugeräten. Durch das stufenlose Getriebe und das schwenkbare Heckzugpendel bleibt die Maschine auch unter Volllast präzise manövrierbar, ohne dass die Ketten beim Lenken den Boden übermäßig aufreißen. Mit einem Einsatzgewicht von bis zu 31 Tonnen und der Integration in das FendtONE-System für die digitale Dokumentation und Spurführung stellt der 1100 Vario MT die Spitze der modernen Landtechnik dar. Er ist spezialisiert auf den Einsatz auf weitläufigen Flächen, wo herkömmliche Radtraktoren aufgrund ihrer physischen Grenzen bei der Zugkraftübertragung und Bodenbelastung ausscheiden. {| class="wikitable" ! ab 2018 ! ab 2022 |- | valign="top" | {{:Traktorenlexikon: Create|1149 Vario}} {{:Traktorenlexikon: Create|1154 Vario}} {{:Traktorenlexikon: Create|1159 Vario}} {{:Traktorenlexikon: Create|1165 Vario}} | valign="top" | {{:Traktorenlexikon: Create|1162 Vario}} {{:Traktorenlexikon: Create|1167 Vario}} |} === Agrobil S === [[File:Fendt Agrobil S.jpeg|thumb|Fendt Agrobil S]] Das Fendt Agrobil markiert den Einstieg von Fendt in die Welt der spezialisierten Ernte-Selbstfahrer und ist das direkte Ergebnis der Übernahme der Maschinenfabrik Dechentreiter im Jahr 1970. Dechentreiter hatte mit dem „Lely-Trac“ bereits Pionierarbeit geleistet, doch erst unter der Führung von Fendt wurde das Konzept zur Serienreife des Agrobil S weiterentwickelt. Das Fahrzeug kombinierte die Funktionen eines Ladewagens mit einem eigenständigen Fahrgestell, um die Futterernte schneller und mobiler zu gestalten. Mit seinem markanten Design – dem vorne sitzenden Fahrerhaus und dem luftgekühlten Deutz-Motor – bot es eine für die 1970er Jahre beeindruckende Transportgeschwindigkeit von bis zu 40 km/h und eine hervorragende Übersicht bei der Aufnahme des Ernteguts. Obwohl das Agrobil aufgrund seiner spezialisierten Ausrichtung und der geringen Ganzjahresauslastung ein Nischenprodukt blieb, legte die Integration der Dechentreiter-Technik den Grundstein für den Ausbau des Standorts Bäumenheim, der heute als Kompetenzzentrum für Kabinenbau eine zentrale Rolle im Konzern spielt. {{Wikipedia1|Agrobil}} {| class="wikitable" | valign="top" | * [[Traktorenlexikon: Fendt Agrobil S|Agrobil S]] 59 kW (80 PS) |} === TriSix === [[Datei:Fendt Trisix.JPG|thumb|Fendt [[Traktorenlexikon: Fendt TriSix|TriSix]]]] Der Fendt Trisix wurde im Jahr 2007 auf der Agritechnica als spektakuläre Konzeptstudie vorgestellt und markierte den Versuch, die Grenzen der Traktorenkonstruktion völlig neu zu definieren. Das auffälligste Merkmal dieser Maschine war das Fahrwerkkonzept mit drei Achsen (6x6-Antrieb), das speziell darauf ausgelegt war, enorme Motorleistung in maximale Zugkraft bei gleichzeitig hoher Bodenschonung und voller Straßentauglichkeit umzusetzen. Technisch basierte der Trisix auf Komponenten der Großtraktoren-Serie, trieb diese jedoch ins Extrem. Ausgestattet mit einem MAN-Sechszylindermotor, der eine Leistung von rund 540 PS erbrachte, nutzte das Fahrzeug zwei stufenlose Vario-Getriebe, um die Kraft auf alle sechs Räder zu verteilen. Durch die drei Achsen erreichte der Trisix eine deutlich größere Aufstandsfläche als herkömmliche Standardtraktoren, was den Bodendruck massiv reduzierte. Ein technisches Highlight war die Lenkung: Die erste und die dritte Achse waren aktiv gelenkt, was dem über 7 Meter langen Gespann eine Wendigkeit verlieh, die man sonst nur von deutlich kleineren Maschinen kannte. Trotz der beeindruckenden Leistungsdaten und des enormen öffentlichen Interesses blieb der Trisix ein reiner Technologieträger. Die Komplexität des Antriebsstrangs, das hohe Eigengewicht von rund 19 Tonnen und die damit verbundenen Produktionskosten standen einer Serienfertigung entgegen. Dennoch war das Projekt für die Entwicklung der Marke von großer Bedeutung: Viele der gewonnenen Erkenntnisse über das Niedrigdrehzahlkonzept, die Steuerung extrem hoher Drehmomente und die Optimierung der Bodenschonung flossen Jahre später direkt in die Entwicklung der 1000 Vario Serie ein. Heute gilt der Trisix als einer der bekanntesten Prototypen der Landtechnikgeschichte, der den Mut der Ingenieure verdeutlicht, radikal neue Wege in der Traktorenarchitektur zu gehen. {| class="wikitable" | valign="top" | * [[Traktorenlexikon: Fendt TriSix|TriSix]] 408 kW (540 PS) |} == Literatur == * Oliver Aust: ''Fendt – Traktoren im Einsatz.'' Podszun, 2004. ISBN 9783861333555. * Peter Glienke, Kai W. Richters: ''Fendt – Schlepper im Einsatz.'' Schwungrad-Verlag, 2006. ISBN 9783933426161. * Klaus Herrmann: ''75 Jahre Dieselross – Eine Erfolgsgeschichte.'' Ulmer, 2005. ISBN 9783800147939. * Klaus Herrmann: ''Die Fendt-Chronik – Vom Dieselross zum Vario.'' 3. Aufl., BLV, 2002. ISBN 9783405157876; 4. Aufl., Ulmer, 2006. ISBN 9783800148639. * Gilbert Kremer: ''Das Fendt Werkstattbuch – Einstelldaten für Fendt-Schlepper – Motoren von MWM und Deutz.'' Verlag Klaus Rabe, 2009. ISBN 9783926071408. * Gilbert Kremer: ''Fendt Geschichte – Das Beste aus der Fendt-Werbung von 1930 bis heute.'' DLG-Verlag, 2009. ISBN 9783769007367. * Gilbert Kremer: ''Alle Traktoren von Fendt – Typen und Daten.'' Verlag Klaus Rabe, 2005. ISBN 9783926071330. * Gilbert Kremer: ''Fendt-Geschichten – Das Beste aus den Fendt-Nachrichten.'' DLG-Verlag, 2003. ISBN 9783769006100. * Gilbert Kremer, Kurt Häfner (Hg.): ''Fendt – Schlepper-Prospekte von 1979 bis 1999.'' Kosmos, 2001. ISBN 9783440081167. * Gilbert Kremer, Kurt Häfner (Hg.): ''Fendt – Schlepper-Prospekte von 1966 bis 1978.'' Kosmos, 1998. ISBN 9783440076187. * Gilbert Kremer, Kurt Häfner (Hg.): ''Fendt – Schlepper-Prospekte von 1930 bis 1966.'' Kosmos, 1997. ISBN 9783440073223. * Albert Mößmer: ''Typenatlas Fendt Traktoren.'' Garamond, 2011. ISBN 9783862456277. * Albert Mößmer: ''Fendt – Das Typenbuch 1928 bis heute.'' Geramond, 2006. ISBN 9783765477010. * Albert Mößmer: ''Fendt Dieselroß - Typengeschichte und Technik.'' GeraMond, 2014. ISBN 9783862455553. * Wolfgang Schiffer: ''Fendt Traktoren – Ackern wie damals – Die Fendt-Legende in Bildern.'' Landwirtschaftsverlag, 2007. ISBN 9783784334516. * Peter Schneider: ''Typenkompass Fendt – Schlepper und Traktoren seit 1974.'' Bd. 2, Motorbuch Verlag, 2010. ISBN 9783613031692. * Peter Schneider: ''Typenkompass Fendt – Schlepper und Traktoren 1928–1993.'' Bd. 1, Motorbuch Verlag, 2010. ISBN 9783613031708. * Klaus Tietgens: ''Die Fendt Geräteträger Chronik.'' Podszun, 2007. ISBN 9783861334187. == Weblinks == {{Commons|Category:Fendt tractors|Fendt-Traktoren}} {{Wikipedia1|Fendt (Marke)}} * [http://www.fendt.com/ www.fendt.com] * [http://www.fendt-oldtimer.de www.fendt-oldtimer.de] Das offizielle Forum zu Fendt-Oldtimern * [http://de.tec24.com/c-10100/m-fendt http://de.tec24.com/c-10100/m-fendt] Gebrauchte Fendt-Traktoren * [https://www.technikboerse.com/gebraucht/traktor-357/fendt-370 technikboerse.com] Gebrauchte Fendt-Traktoren auf technikboerse.com * [http://www.oldtimerplus.de/blog/tag/fendt/ www.oldtimerplus.de/blog/tag/fendt/] Blog mit Infos für Fendt-Fans Tabellarische Typen, Übersicht vieler Fendt-Schlepper: www.tractorbook.de/traktoren/fendt * http://www.fendt-freunde.de/datenbank/fs_dat1.htm * http://www.fendt-freunde.de/datenbank/fs_dat2.htm == Einzelnachweise == <references /> {{:Traktorenlexikon: Navigation}} gh13i3x9yd5964i7tmi2bvbzp3nuoev Latein/ Grammatik/ Verb 0 17461 1092064 805038 2026-10-10T19:24:50Z ~2026-54318-83 117027 dt Bedeutungen der irregulären verben hinzugefügt. 1092064 wikitext text/x-wiki == '''Die lateinischen Verben''' == Die lateinischen Verben werden in 5 unterschiedliche Hauptgruppen eingeteilt. Drei dieser Gruppen werden nach dem Stammvokal (drittletzter Buchstabe des Infinitivs) benannt. Dieser Stammvokal wird '''lang''' ausgesprochen. A-Konjugation Beispiele: amare, cantare E-Konjugation Beispiele: carere, timere I-Konjugation Beispiele: venire, scire Als weitere Gruppe gibt es die konsonantische Konjugation. Hier endet der Stamm auf einen Konsonanten und ein '''kurz''' gesprochenes "e" ersetzt den Stammvokal. konsonantische Konjugation Beispiele: agere, regere Die gemischte (oder auch Kurz-i) Konjugation ist eigentlich eine Untergruppe der konsonantischen Konjugation. In den meisten Grammatiken wird sie allerdings der Systematisierung wegen als eigene Konjugation geführt. Die Formen entsprechen der I-Konjugation bis auf den Infinitiv Präsens, den Imperativ 1 Singular und den Konjunktiv Imperfekt (hier wird das lang gesprochene "i" durch ein kurzes "e" ausgetauscht). gemischte Konjugation Beispiele: capere, fugere Daneben werden einige Verben unregelmäßig gebildet. Es handelt sich dabei um Verben, die in der Alltagssprache sehr häufig vorkommen. Beispiele sind: esse (und Komposita) -> sein posse -> können velle, nolle, malle -> wollen, nicht wollen, lieber wollen ire -> gehen ferre -> tragen, bringen fieri -> geschehen, werden c2e0hq7ba8fkkpj817iyr0uz09p2rqr Armenisch/ Lautlehre 0 18033 1092065 594033 2026-10-10T20:43:00Z ~2026-54824-92 117028 /* Verschlusslaute */ Präsens 1092065 wikitext text/x-wiki {{Navigation hochvor |hochlink=Armenisch|hochtext=Inhaltsverzeichnis |vorlink=Armenisch/ Übung: Aussprache einzelner Wörter|vortext=Übung: Aussprache einzelner Wörter}} ''Anmerkung: In diesem Abschnitt werden Zeichen des Internationalen Phonetischen Alphabets benutzt, man erkennt sie an den eckigen Klammern.'' [[Image:Manrankar.jpg|200px|thumb|right|Manuskript aus dem frühen Mittelalter]] Dieses Kapitel handelt von den Lauten der armenischen Sprache und der schriftliche Umsetzung dieser Laute. Die armenische Sprache besitzt sehr viele Konsonanten (Mitlaute), von denen es einige im Deutschen nicht gibt, während die Vokale (Selbstlaute) entweder bekannt sind oder der Unterschied nur fein ist. Einige Dialekte des Armenischen besitzen Ejektive, was für indogermanische Sprachen untypisch ist und vermutlich auf den Einfluss der Umgebungssprachen zurückzuführen ist. Sie sind hier nicht weiter erwähnt. ==Vokale (vokalische Approximanten)== Ein Approximant (Näherungsslaut) ist ein nach seiner Artikulationsart benannter Laut. Zu den Approximanten gehören alle Vokale und diejenigen Konsonanten, die mit pulmonisch-egressiver oder pharyngaler Luft gebildet werden und im Ansatzrohr keinen Verschluss oder eine Enge mit „Reibung“ (Geräuschentwicklung, Luftverwirbelungen) überwinden müssen [http://de.wikipedia.org/wiki/Approximant]. Im Armenischen gibt es sechs Vokale. Anders als im Deutschen gibt es bei Vokalen am Wortanfang '''nie Knacklaute.''' Die armenischen Schrift gibt die Aussprache der Vokale sehr genau wieder. Anders als im Deutschen, wo zum Beispiel die vier ''e'' in reden [ʀedən] und Retter [ʀɛtʰɐ̯] vier verschiedene Laute wiedergeben, kann man sich im Armenischen mit Ausnahme ganz weniger Ausnahmen auf eine einheitliche Aussprache verlassen. Im Armenischen sind alle Vokale ungerundet (mit Ausnahme des Schwas, das weder gerundet noch ungerundet ausgesprochen werden kann), während im Deutschen sowohl gerundete als auch gerundete Vokale vorkommen. „Rundung“ heißt hierbei, dass die Lippen leicht gespitzt werden. Der Klangunterschied bei der Aussprache des deutschen [o] statt des armenischen [ɤ] und des deutschen [u] statt des armenischen [ɯ] ist allerdings eher fein. {| border="1" cellspacing="0" cellpadding="5" style="border-collapse:collapse; float:right; margin-left:5ex; margin-bottom: 2ex;" | rowspan="2" | &nbsp; ! colspan="2" style="background-color:#FFDEAD" | vorne ! colspan="2" style="background-color:#FFDEAD" | fast<br />vorne ! colspan="2" style="background-color:#FFDEAD" | zentral ! colspan="2" style="background-color:#FFDEAD" | fast<br />hinten ! colspan="2" style="background-color:#FFDEAD" | hinten |- style="text-align:center" | style="width:2em; background-color:#FFDEAD" | ung. | style="width:2em; background-color:#FFDEAD" | ger. | style="width:2em; background-color:#FFDEAD" | ung. | style="width:2em; background-color:#FFDEAD" | ger. | style="width:2em; background-color:#FFDEAD" | ung. | style="width:2em; background-color:#FFDEAD" | ger. | style="width:2em; background-color:#FFDEAD" | ung. | style="width:2em; background-color:#FFDEAD" | ger. | style="width:2em; background-color:#FFDEAD" | ung. | style="width:2em; background-color:#FFDEAD" | ger. |- ! style="background-color:#FFDEAD; text-align:left" | geschlossen | title="Unicode-Code: U+0069 (i)" style="text-align:center" | i | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | title="Unicode-Code: U+026F" style="text-align:center" | ɯ | &nbsp; |- ! style="background-color:#FFDEAD; text-align:left" | fast geschlossen | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; |- ! style="background-color:#FFDEAD; text-align:left" | halbgeschlossen | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | title="Unicode-Code: U+0264" style="text-align:center" | ɤ | &nbsp; |- ! style="background-color:#FFDEAD; text-align:left" | mittel | &nbsp; | &nbsp; | &nbsp; | &nbsp; | colspan="2" title="Unicode-Code: U+0259" style="text-align:center" |ə | &nbsp; | &nbsp; | &nbsp; | &nbsp; |- ! style="background-color:#FFDEAD; text-align:left" | halboffen | title="Unicode-Code: U+025B" style="text-align:center" | ɛ | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; |- ! style="background-color:#FFDEAD; text-align:left" | fast offen | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; |- ! style="background-color:#FFDEAD; text-align:left" | offen | title="Unicode-Code: U+0061 (a)" style="text-align:center" | a | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; |} ''Anmerkung zur Tabelle: Die Abkürzung „ger.“ steht für gerundet, „unger.“ für ungerundet, siehe auch den Wikipedia-Artikel zum Thema [http://de.wikipedia.org/wiki/Rundung_%28Phonetik%29] ===Ungerundeter offener Vorderzungenvokal ([a])=== Der Vokal [a] wird wie in D'''a'''ch ausgesprochen und wird auf armenisch ա (klein) bzw. Ա (groß) geschrieben. Beispiele: նա (na) - er/sie/es, jener/jene/jenes; սա (sa) – das (hier), dies (hier); դաս (das) – Unterricht, Lektion ===Ungerundeter halboffener Vorderzungenvokal ([ɛ])=== Der Vokal [ɛ] wird wie in St'''e'''lle oder G'''ä'''nse oder auch das französische è und '''''nicht''''' wie in r'''e'''den ausgesprochen ''(dieser'' Vokal wird im Internationalen Phonetischen Alphabet als [e] wiedergegeben). Er wird auf armenisch durch է (klein) bzw. Է (groß) wiedergegeben. Der Buchstabe ե (klein) bzw. Ե (groß) wird am Wortanfang wie [jɛ] ausgesprochen, im Wort einfach als [ɛ]. Beispiele: է (ē) – ist, եւ (ew) – und, սեւ (sew) – schwarz, Außerdem taucht der Laut [ɛ] auch noch in der Ligatur և (e͡w, am Wortanfang [jɛv], sonst [ɛv]) auf, die aus den Teilen ե (e) ւ (w) zusammengesetzt ist. Man kann also auch schreiben: և, սև ===Ungerundeter geschlossener Vorderzungenvokal ([i])=== Der Vokal [i] wird wie im Deutschen ausgesprochen. Man schreibt ihn auf armenisch ի (klein) bzw. Ի (groß). Beispiele: մի (mi) – ein/eine/eines//einige, հիմա (hima) – jetzt, իմանալ (imanal) – wissen, գինի (gini) - Wein ===Schwa (mittlerer Zentralvokal, [ə])=== Das Schwa ([ə]) wird auf armenisch ը (klein) bzw. Ը (groß) geschrieben. Dieser Buchstabe wird wie in Rüb'''e''' ausgesprochen. Beispiele: ինը (ině) – neun ===Ungerundeter geschlossener Hinterzungenvokal ([ɯ])=== Der Laut [ɯ], der sich im Armenischen aus dem Diphthong [ow] entwickelt hat, wird als ու (klein, ow) bzw. Ու (groß, Ow) geschrieben. Er ist dem [u] im Deutschen sehr ähnlich Beispiele: դու (dow) – du, ում (owm) – wessen, ու (ow) – und ===Halbgeschlossener hinterer Vokal ([ɤ])=== ''Anmerkung: In diesem Abschnitt kommen in den Beispielen einige Buchstaben vor, deren Aussprache noch nicht genau erläutert wurde, dies wird später nachgeholt.'' Der Selbstlaut [ɤ] wird im Armenischen nur wenig anders ausgesprochen als das [o] im Deutschen, die Lippen sind etwas weniger gespitzt als im Deutschen, ihre Haltung ist eher wie beim [a]. Das armenische o ist in Bezug auf die Schreibung der komplizierteste Buchstabe: er wird normalerweise als ո (klein) bzw. Ո (groß) geschrieben, in seltenen Fällen auch als օ (klein) bzw. Օ (groß). '''1.''' Das armenische ո (transliteriert als o) wird im ganz normal ausgesprochen, am Wortanfang aber wie ''wo'' ([vɤ]). Der Sinn dieser Regelung ist, dass das [v] bei der Aussprache zusammengesetzte Wörter ausfällt, z.B. որդի (ordi, [vɤɹ'tʰi] (Sohn)), aber քեռորդի (k‘eṙordi, [kʰerɤɹ'tʰi] (Sohn des Onkels mütterlicherseits)). Beispiele: տոմս (toms, [tɤms]) – Ausweis, Fahrausweis, Eintrittskarte; բոլոր (bolor, [bɤ'lɤɹ]) – alle, սովորել (sovorel, [sɤvɤ'ɹɛl]) - lernen, studieren; խոտ (xot, [xɤt]) – Gras, Heu *Die einzige Ausnahme bilden zwei sehr wichtige Wörter, die ''wer'' bedeuten: ով (ov, Einzahl) und ովքեր (ovk‘er, Mehrzahl), die als [ɤv] bzw. [ɤv'kʰɛɹ] gelesen werden. '''2.''' Im 13. Jahrhundert wurde der Buchstabe օ (transliteriert als ō) hinzugefügt, um ausländische, d.h. vor allem griechische, Wörter richtig wiederzugeben. Beispiele: Օլիմպիա (O̅limpia, [ɤlim'pia]) – Olympia, օպերա (ōpera, [ɤpɛ'ɹa]) – Oper *Ebenfalls im Mittelalter ersetzte der neue Buchstabe օ (ō) auch die Buchstabenkombination աւ (aw) dann, wenn auf sie ein Konsonant folgt, um sich der veränderten Aussprache [o] anzupassen. Beispiele: օր (ōr, [ɤɹ], früher աւր) – Tag, օրինակ (orinak, [ɤɹi'nak]) - Beispiel **In zusammengesetzten Wörtern kann der Buchstabe օ (ō) auch innerhalb des Wortes auftauchen. Beispiel: այսօր (aysōr, [ajsɤɹ]) – heute (von այս օր – dieser Tag) Abschlussbeispiel: Բոլորը որոշել են սովորել օտար լեզուներ: (Bolorě orošel en sovorel ōtar lesowner.) – Alle haben beschlossen, Fremdsprachen zu lernen. ==Konsonantische Approximanten== ===Stimmhafter palataler Approximant ([j])=== Im Neuarmenischen gibt es nur den einen Halbvokal [j], der dem deutschen ''j'' entspricht: յ (klein) Յ (groß). ===Stimmhafter palataler Approximant ([l])=== Im Armenischen gibt es einen Laterallaut, [l]. Er entspricht dem deutschen l. Man schreibt ihn als լ (klein) bzw. Լ (groß). ===Stimmhafter alveolarer Approximant ([ɹ])=== Die richtige Aussprache der armenischen Variante des Lautes [ɹ] bereitet deutschen Muttersprachlern Probleme. Dieser Laut ähnelt dem englischen ''r,'' tendiert aber etwas in Richtung [l] und insbesondere am Wortende oder vor Konsonanten [ʒ] (dieses Zeichen meint den Laut in Gara'''g'''e oder plea'''s'''ure). Man schreibt ihn als ր (klein) bzw. Ր (groß). ==Verschlusslaute== {|border="1" cellspacing="0" cellpadding="5" style="border-collapse: collapse; float: right; margin-left: 1ex;" |+'''Verschlusslaute''' |- ! !Labial !Dental !Velar |- |stimmhaft||բ (b, [b])||դ (d, [d])||գ (g, [g]) |- |stimmlos||պ (p, [p])||տ (t, [t])||կ (k, [k]) |- |stimmlos aspiriert||փ [p‘, [pʰ])||թ (p‘, [tʰ])||ք (k‘, [kʰ]) |} Verschlusslaute (Plosive) sind im Armenischen dreistufig (stimmhaft, stimmlos und stimmlos aspiriert). Hochdeutsche Muttersprachler nehmen nur zweistufige wahr (stimmhaft und stimmlos aspiriert) und brauchen etwas Zeit, um die Unterschiede erst bewusst wahrzunehmen und dann auch selbst auszusprechen. Vereinfacht gesagt sprechen Deutsche beim p, t und k immer ein h mit, während sie das bei b, d und g nicht tun. Im Alt- und Ostarmenischen gibt es zusätzlich noch eine unbehauchte Variante (also ohne h) von p, t und k. Dies entspricht der unbehauchten Aussprache von '''p'''izza (italienisch), '''c'''oleur (französisch) oder '''т'''ам (russisch). Beim Transliterieren wird die Aspiration durch das Hinzufügen des Zeichens ‘ ausgedrückt. Bei einer Reihe von Wörtern hat sich die Aussprache der Verschlusslaute über die Zeit verändert, während die Schreibung unverändert geblieben ist (meistens nicht am Wortanfang sondern in der Mitte oder im Auslaut). Dies wird bei der erstmaligen Erwähnung eines Wortes vermerkt. Beispiele: օդ (ōd, [otʰ]) – Luft, շաբաթ (šabat‘, [ʃapʰatʰ]) – Sonntag/Sonnabend (Sabbat), կարդալ (kardal, [kaɹtal]), երբ (erb, [jɛɹpʰ]) – wer (bezügliches Fürwort/Relativpronom), սուգ (sowg, [sukʰ]) – Trauer ==Reibelaute== {|border="1" cellspacing="0" cellpadding="5" style="border-collapse:collapse; float:right; margin-left:1ex;" |+'''Reibelaute''' ! !Labial !Dental !Postalveolar !Uvular !Glottal |- |stimmhaft||վ (v, [v])||զ (z, [z])||ժ (ž, [ʒ])|| || |- |stimmlos||ֆ (f, [f])||ս (s, [s])||շ (š, [ʃ])||խ (x, [x])||հ (h, [h]) |- |} Die armenischen Reibelaute (Frikative) bereiten Deutschen keine Schwierigkeiten. Wichtig ist nur: der Buchstabe խ (klein) bzw. Խ (groß) gibt den so genannten Ach-Laut ([x]) wieder (wie in Ba'''ch'''), er wird '''''nie''''' wie der so genannte Ich-Laut [ç] (wie z.B. in Mäd'''ch'''en) ausgesprochen. Eine Besonderheit ist der Buchstabe ւ (klein, transliteriert als w) bzw. Ւ (groß, W). Im Altarmenischen stand er vermutlich zwischen [v] und [u] oder gab den Halbvokal [w] wieder (wie in englisch ''w''ater oder italienisch L''u''igi). Im Neuarmenischen wird er im Allgemeinen als [v] ausgesprochen, in Verbund mit dem Buchstaben ո wird dieser ursprüngliche Dipthong ու heute wie [u] ausgesprochen. ==Affrikaten== {|border="1" cellspacing="0" cellpadding="5" style="border-collapse:collapse; float:right; margin-left:5ex; margin-bottom: 2ex;" |+'''Affrikaten''' ! !Alveolar !Palatal |- |stimmhaft||ձ (j, [d͡z])||ջ (ǰ, [d͡ʒ]) |- |stimmlos||ծ (c [t͡s])||ճ (č, [t͡ʃ]) |- |stimmlos aspiriert||ց (c‘ [t͡sʰ])||չ (č‘,[t͡ʃʰ]) |- |} Das Armenische kennt sechs Affrikaten (Verbindung von jeweils einem Verschluss- und einem homorganen Reibelaut zu einem Phonem). Die Dreistufigkeit bereitet deutschen Muttersprachlern teilweise größere Schwierigkeiten, insbeondere die Aussprache von ծ (c, [t͡s]) und ճ (č, [t͡ʃ]). Bei einer Reihe von Wörtern hat sich die Aussprache der Verschlusslaute über die Zeit verändert, während die Schreibung unverändert geblieben ist (meistens nicht am Wortanfang sondern in der Mitte oder im Auslaut). Dies wird bei der erstmaligen Erwähnung eines Wortes vermerkt. Beispiel: աղջիկ (ałǰik, [aʀt͡ʃʰik]) – Mädchen ==Nasenlaute== Die armenischen Nasenlaute (Nasale) unterscheiden sich nicht von den deutschen. * Das armenische [m] entspricht genau dem deutschen. Die entsprechenden Buchstaben sind մ (klein) bzw. Մ (groß). * ն bzw. Ն entsprechen genau wie n bzw. N zwei verschiedenen Lauten: [n] (Rah'''n''', '''n'''iema'''n'''d) und [ŋ] (Ra'''n'''g, Kla'''n'''g). Hierdurch entstehen aber für deutsche Muttersprachler keinerlei Schwierigkeiten beim Sprechen, denn sie wählen intuitiv den richtigen Laut aus. ==Vibranten== Im Armenischen gibt es zwei Vibranten. Mit beiden sind deutsche Muttersprachler vertraut. * [ʀ] ist die übliche hochdeutsche Aussprache des deutschen ''r,'' diese Aussprache findet sich auch im Französischen. Man schreibt es als ղ bzw. Ղ. * [r] steht im Internationalen Phonetischen Alphabet für das ''gerollte r,'' wie es im deutschen Sprachraum u.a. in Süddeutschland, Österreich, der Schweiz und im Niederdeutschen (Plattdüütsch) gesprochen wird, außerhalb des deutschen Sprachraums trifft es z.B. im Italienischen, Spanischen und Russischen auf. Die entsprechenden armenischen Buchstaben sind ռ bzw. Ռ. ==Zusammenfassende Tabelle der Konsonanten== Die folgende Tabelle enthält alle armenische Konsonaten mit Ausnahme der Affrikaten (und der Ejektive). {| {{Prettytable}} ! rowspan="2" | &nbsp; ! colspan="2" style="background-color:#FFDEAD" | bilabial ! colspan="2" style="background-color:#FFDEAD" | labiodental ! colspan="2" style="background-color:#FFDEAD" | dental ! colspan="2" style="background-color:#FFDEAD" | alveolar ! colspan="2" style="background-color:#FFDEAD" | postalveolar ! colspan="2" style="background-color:#FFDEAD" | retroflex ! colspan="2" style="background-color:#FFDEAD" | palatal ! colspan="2" style="background-color:#FFDEAD" | velar ! colspan="2" style="background-color:#FFDEAD" | uvular ! colspan="2" style="background-color:#FFDEAD" | pharyngal ! colspan="2" style="background-color:#FFDEAD" | glottal |- style="text-align:center" | style="width:1.7em; background-color:#FFDEAD" | stl. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | sth. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | stl. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | sth. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | stl. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | sth. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | stl. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | sth. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | stl. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | sth. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | stl. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | sth. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | stl. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | sth. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | stl. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | sth. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | stl. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | sth. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | stl. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | sth. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | stl. | style="width:1.7em; background-color:#FFDEAD; text-align:center" | sth. |- ! style="background-color:#FFDEAD; text-align:left" | Plosive | title="Unicode-Code: U+0070 (p)" style="text-align:center" | p | title="Unicode-Code: U+0062 (b)" style="text-align:center" | b | &nbsp; | &nbsp; | &nbsp; | &nbsp; | title="Unicode-Code: U+0074 (t)" style="text-align:center" | t | title="Unicode-Code: U+0064 (d)" style="text-align:center" | d | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | title="Unicode-Code: U+006B (k)" style="text-align:center" | k | title="Unicode-Code: U+0067 (g)" style="text-align:center" | g | &nbsp; | &nbsp; | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | &nbsp; | style="background-color:#D8D8D8" | &nbsp; |- ! style="background-color:#FFDEAD; text-align:left" | Nasale | &nbsp; | title="Unicode-Code: U+006D (m)" style="text-align:center" | m | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | title="Unicode-Code: U+006E (n)" style="text-align:center" | n | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | title="Unicode-Code: U+014B" style="text-align:center" | ŋ | &nbsp; | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; |- ! style="background-color:#FFDEAD; text-align:left" | Vibranten | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | title="Unicode-Code: U+0072 (r)" style="text-align:center" | r | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | &nbsp; | title="Unicode-Code: U+0280" style="text-align:center" | ʀ | &nbsp; | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; |- ! style="background-color:#FFDEAD; text-align:left" | Taps/Flaps | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; |- ! style="background-color:#FFDEAD; text-align:left" | Frikative | &nbsp; | &nbsp; | title="Unicode-Code: U+0066 (f)" style="text-align:center" | f | title="Unicode-Code: U+0076 (v)" style="text-align:center" | v | &nbsp; | &nbsp; | title="Unicode-Code: U+0073 (s)" style="text-align:center" | s | title="Unicode-Code: U+007A (z)" style="text-align:center" | z | title="Unicode-Code: U+0283" style="text-align:center" | ʃ | title="Unicode-Code: U+0292" style="text-align:center" | ʒ | &nbsp; | &nbsp; | &nbsp; | &nbsp; | title="Unicode-Code: U+0078 (x)" style="text-align:center" | x | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | title="Unicode-Code: U+0068 (h)" style="text-align:center" | h | &nbsp; |- ! style="background-color:#FFDEAD; text-align:left" | laterale Frikative | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; |- ! style="background-color:#FFDEAD; text-align:left" | Approximanten | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | title="Unicode-Code: U+0279" style="text-align:center" | ɹ | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | title="Unicode-Code: U+006A (j)" style="text-align:center" | j | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; |- ! style="background-color:#FFDEAD; text-align:left" | laterale&nbsp;Approximanten | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | &nbsp; | &nbsp; | &nbsp; | title="Unicode-Code: U+006C (l)" style="text-align:center" | l | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; | style="background-color:#D8D8D8" | &nbsp; |} Die Abkürzung ''stl.'' steht hier für stimmlos und ''sth.'' für stimmhaft. Dunkel hinterlegte Felder bezeichnen physiologisch unmögliche Artikulationen. Beispielsweise ist ein glottaler Nasal unmöglich, weil bei einem Verschluss der Stimmlippen keine Luft durch die Nase ausströmen kann. hykarbw975zu3mky7roo9ngv2tr0iqp Mathe für Nicht-Freaks: Axiomatische Mengenlehre 0 104730 1092063 1080203 2026-10-10T18:42:08Z Jürgen-Michael Glubrecht 69138 /* Aussonderung */ 1092063 wikitext text/x-wiki {{#invoke:Mathe für Nicht-Freaks/Seite|oben}} Es wurden verschiedene axiomatische Mengenlehren entwickelt. Die aktuell gebräuchlichste ist die [[w:Zermelo-Fraenkel-Mengenlehre|Zermelo-Fraenkel-Mengenlehre]] '''ZFC'''. Sie ist nach den beiden deutschen Mathematikern [[w:Ernst Zermelo|Ernst Zermelo]] und [[w:Adolf Abraham Halevi Fraenkel|Abraham Fraenkel]] benannt, die sie von 1908 bis 1922 entwickelten. Das '''C''' in der Bezeichnung steht für "Axiom of Choice" (Auswahlaxiom). Wie am Ende des Kapitels [[Mathe für Nicht-Freaks: Russells Antinomie und Klassen|"Russells Antinomie und Klassen"]] beschrieben, legen wir eine klassentheoretische Sprache zugrunde, in der beliebige Klassen mit dem Klassenbildungsoperator <math>\{ x \, | \, \dots \}</math> gebildet werden können. Insbesondere ist <math>\mathcal{V} := \{ x \, | \, x = x \}</math> die ''Allklasse'', für deren Elemente auch die Variablen <math>x, y, z, \dots</math> stehen, und <math>\mathcal{R} := \{ x \, | \, x \notin x \}</math> die ''Russellsche Klasse''. {{:Mathe für Nicht-Freaks: Vorlage:Hinweis|Wir nennen die Klasse <math>\mathcal{K}</math> dann eine '''Menge''', wenn sie im Variblenbereich, also in der Allklasse liegt: <math>\mathcal{K} \in \mathcal{V}</math>.}} Wir wissen bereits, dass nicht ''alle'' Klassen Mengen sind, denn es gilt ja <math>\mathcal{R} \notin \mathcal{V}</math>. Die Axiome von ZFC sorgen nun dafür, dass viele Klassen Mengen sind. So viele, dass sie für die Mathematik ausreichen. Um die Axiome zu verstehen, hilft die Vorstellung, dass Mengen ''kleine'' Klassen sind. == Extensionalität == Das erste Axiom gilt bereits für Klassen: {{rot|'''Extensionalitätsaxiom'''|LightSeaGreen}} {{Formel|<math>\begin{align} & \forall x : (x \in \mathcal{M} \Leftrightarrow x \in \mathcal{N}) \Rightarrow \mathcal{M} = \mathcal{N} \\ & \color{Black} \text{Zwei Klassen – und damit auch zwei Mengen! – sind gleich, wenn sie dieselben Elemente haben.} \end{align}</math>}} Die Extensionalität haben wir bereits im [[Mathe für Nicht-Freaks: Mengenlehre: Menge#Die Extensionalität von Mengen|1. Kapitel der Mengenlehre]] kennen gelernt. Die umgekehrte Richtung (<math>\Leftarrow</math>) gilt auch, das ergibt sich aus den Eigenschaften von <math>=</math>. {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Folgerung |frage= <math>\mathcal{K}</math> sei eine beliebige Klasse. Dann gilt: <math>\mathcal{K} = \{ x \, | \, x \in \mathcal{K} \}</math> |antwort= Wir setzen <math>x \in \mathcal{K}</math> für <math>A(x)</math>. Dann folgt aus dem Abstraktionsprinzip <math>\forall y : (y \in \mathcal{K} \Leftrightarrow y \in \{ x \,| \, x \in \mathcal{K} \})</math>. Mit der Extensionalität erhalten wir <math>\mathcal{K} = \{ x \, | \, x \in \mathcal{K} \}</math>. ✔ }} {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Verständnisfrage |frage= <math>\mathcal{K}</math> sei eine echte Klasse. Dann gilt: <math>\{ \mathcal{K} \} = \varnothing</math> |antwort= Nach Definition der Einerklasse gilt <math>\{ \mathcal{K} \} = \{ x \, | \, x = \mathcal{K} \}</math> und die Elemente von Klassen sind Mengen. <math>\mathcal{K}</math> ist aber eine echte Klasse, es gilt also <math>\forall x : x \ne \mathcal{K}</math>. ✔ }} == Leere Menge == Die leere Klasse hat keine Elemente und ist wie folgt definiert: <math>\varnothing := \{ x \, | \, x \ne x \}</math>. Nach dem Extensionalitätsaxiom gibt es nur eine leere Klasse, denn wenn <math>\mathcal{K}</math> ebenfalls keine Elemente hat, gilt <math>\forall x : (x \in \mathcal{K} \Leftrightarrow x \in \varnothing)</math> und daraus folgt <math>\mathcal{K} = \varnothing</math>. <span style="color:LightSeaGreen">'''Axiom der leeren Menge'''</span> {{Formel|<math>\begin{align} & \varnothing \in \mathcal{V} \\ & \color{Black} \text{Die leere Klasse ist eine Menge.} \end{align}</math>}} {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Verständnisfrage |frage= Zeige <math>\mathcal{K} \ne \varnothing \Rightarrow \exists x : x \in \mathcal{K}</math> für eine beliebige Klasse <math>\mathcal{K}</math>. |antwort= Da <math>\mathcal{K} \ne \varnothing</math> folgt mit dem Extensionalitätsaxiom <math>\neg \forall x : (x \in \mathcal{K} \Leftrightarrow x \in \varnothing)</math>. [[Mathe für Nicht-Freaks: Aussagen negieren|Logische Umformung]] liefert <math>\exists x : \neg (x \in \mathcal{K} \Leftrightarrow x \in \varnothing)</math>. Weil <math>x \in \varnothing</math> immer ''falsch'' ist, können wie das nach den [[Mathe für Nicht-Freaks: Gesetze der Logik|Regeln der Aussagenlogik]] zu <math>\exists x : x \in \mathcal{K}</math> verkürzen. }} == Paarmengen == Nicht nur die leere Klasse, auch Klassen mit zwei Elementen sind klein. Das folgende Axiom stellt sicher, dass sie Mengen sind: <span style="color:LightSeaGreen">'''Paarmengenaxiom'''</span> {{Formel|<math>\begin{align} & \forall x, y : \{ x, y \} \in \mathcal{V} \\ & \color{Black} \text{Alle Klassen mit zwei Elementen sind Mengen.} \end{align}</math>}} {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Folgerung |frage= Auch die Klassen mit einem Element sind Mengen: <math>\forall x : \{ x \} \in \mathcal{V}</math>. |antwort= Wir wählen im Paarmengen-Axiom <math>x = y</math> und erhalten <math>\{ x, x \} = \{ x \} \in \mathcal{V}</math>.}} Mit Hilfe des Paarmengenaxioms lassen sich ''geordnete Paare'' definieren. Geordnete Paare sind die Elemente des [[Mathe für Nicht-Freaks: Kartesisches Produkt|kartesischen Produkts]] <math>\mathcal{A} \times \mathcal{B} := \{ (x, y) \, | x \in \mathcal{A} \land y \in \mathcal{B} \}</math>. Während die Reihenfolge der Elemente bei den Paarmengen keine Rolle spielt, ist sie bei geordneten Paaren wesentlich. Im allgemeinen ist <math>(a, b) \ne (b, a)</math> und <math>(a, a) \ne a</math>. Zwei geordnete Paare sind nur dann gleich, wenn beide Komponenten gleich sind. Weiterhin sollen geordnete Paare Mengen sein, denn sie sollen ja als Elemente verwendet werden! Insgesamt soll also gelten: # <math>a, b \in \mathcal{V} \Rightarrow (a, b) \in \mathcal{V}</math> # <math>(a_1, b_1) = (a_2, b_2) \Rightarrow a_1 = a_2 \land b_1 = b_2</math> Die heute übliche Paardefinition in der Mengenlehre stammt vom polnischen Mathematiker [[w:Kazimierz Kuratowski|Kazimierz Kuratowski]] und ist nach ihm benannt. Die Idee ist folgende: die erste Komponente des geordneten Paares <math>(a, b)</math> wird als Einermenge, die zweite als Zweiermenge dargestellt und beides wird dann in einer Zweiermenge zusammengefasst: {{:Mathe für Nicht-Freaks: Vorlage:Definition |titel=Kuratowski-Paare |definition=Für zwei Klassen <math>\mathcal{A}</math> und <math>\mathcal{B}</math> ist das ''geordnete Paar'' so definiert: {{Formel|<math>(\mathcal{A}, \mathcal{B}) := \{ \{\mathcal{A} \}, \{\mathcal{A}, \mathcal{B} \} \}</math>}} }} Wir haben die Definition für beliebige Klassen definiert, aber wenn echte Klassen beteiligt sind, funktioniert das nicht wie gewünscht. Ist beispielsweise <math>\mathcal{B}</math> eine echte Klasse, so liefert sie zur Zweiermenge keinen Beitrag, denn es gilt: <math>\{ \mathcal{A}, \mathcal{B} \} = \{ x \, | \, x = \mathcal{A} \lor x = \mathcal{B} \} = \{ x \, | \, x = \mathcal{A} \} = \{ \mathcal{A} \}</math>. Für zwei Mengen dagegen erfüllt das Kuratowski-Paar die Anforderungen: {{:Mathe für Nicht-Freaks: Vorlage:Satz |titel=Eigenschaft geordneter Paare |satz=Sind <math>a, b, a_1, a_2, b_1, b_2 \in \mathcal{V}</math>, so gilt: # <math>(a, b) \in \mathcal{V}</math> # <math>(a_1, b_1) = (a_2, b_2) \Rightarrow a_1 = a_2 \land b_1 = b_2</math> |beweis=Die erste Behauptung folgt unmittelbar aus dem Paarmengenaxiom. {{:Mathe für Nicht-Freaks: Vorlage:Fallunterscheidung |fall1=<math>a_1 = b_1</math> |beweis1= Dann ist <math>(a_1, a_1) = \{ \{a_1\}, \{a_1, a_1\} \} = \{\{a_1\}\}</math> eine Einermenge. Dann ist auch <math>(a_2, b_2)</math> eine Einermenge und daher ist <math>a_2 = b_2</math> und <math>\{\{ a_1 \}\} = \{\{ a_2 \}\}</math>. Also gilt <math>a_1 = a_2 = b_1 = b_2</math>. |fall2=<math>a_1 \ne b_1</math> |beweis2= Dann enthält <math>(a_1, b_1)</math> eine Einer- und eine Zweiermenge. Das gilt dann auch für <math>(a_2, b_2)</math>. Aus der Gleichheit der Paarmengen folgt dann die Gleichheit der Einermengen <math>\{ a_1 \} = \{ a_2 \}</math> und daraus <math>a_1 = a_2</math>. Mit dieser Gleichheit und der Gleichheit der Zweiermengen <math>\{ a_1, b_1 \} = \{ a_2, b_2 \}</math> ergibt sich <math>\{ a_1, b_1 \} = \{ a_1, b_2 \}</math> und daraus <math>b_1 = b_2</math>. ✔ }}}} Mit Hilfe von geordneten Paaren kann die gesamte Theorie der [[Mathe für Nicht-Freaks: Relation|Relationen]] und [[Mathe für Nicht-Freaks: Abbildung, Funktion|Funktionen]] entwickelt werden. == Vereinigung == Wir haben die [[Mathe für Nicht-Freaks: Vereinigung von Mengen|große Vereinigung]] von <math>\mathcal{K}</math> so definiert: <math>\bigcup \mathcal{K} := \{ x \, | \, \exists y \in \mathcal{K} : x \in y \}</math>. Die Vereinigung sammelt also die Elemente der Elemente von <math>\mathcal{K}</math>. Die Vereinigung einer Zweierklasse ist gerade die Vereinigung der beiden Elemente: <math>\bigcup \{x, y\} = x \cup y</math> <span style="color:LightSeaGreen">'''Vereinigungsmengenaxiom'''</span> {{Formel|<math>\begin{align} & \forall x : \bigcup x \in \mathcal{V} \\ & \color{Black} \text{Die Vereinigung einer Menge ist wieder eine Menge.} \end{align}</math>}} Insbesondere ist <math>x \cup y</math> eine Menge. {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Aufgabe |frage= Zeige mit Hilfe des Axiom für Paarmengen: <math>\bigcup \mathcal{V} = \mathcal{V}</math>. |antwort= Für jede Klasse gilt <math>\mathcal{K} \subseteq \mathcal{V}</math>, also auch <math>\bigcup \mathcal{V} \subseteq \mathcal{V}</math>. Sei nun <math>x \in \mathcal{V}</math>. Aus dem Axiom für Paarmengen folgt <math>\{ x \} \in \mathcal{V}</math>, also gibt es eine Menge aus <math>\mathcal{V}</math>, nämlich <math>\{ x \}</math> mit <math>x \in \{ x \} \in \bigcup \mathcal{V}</math>. Das zeigt <math>\mathcal{V} \subseteq \bigcup \mathcal{V}</math>. Beides zusammen ergibt <math>\bigcup \mathcal{V} = \mathcal{V}</math> ✔ }} {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Verständnisfrage |frage= Es gilt <math>\bigcup \varnothing = \varnothing</math>. Werden für den Beweis Axiome für Mengen benötigt? |antwort= Nein, denn die leere Klasse hat keine Elemente, also <math>\bigcup \varnothing</math> auch nicht. ✔ }} {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Aufgabe |frage= Zeige: <math>\forall x_1, x_2, \dotsc, x_n : \{x_1, x_2, \dotsc, x_n \} \in \mathcal{V}</math>. |antwort= Die Einermengen <math>\{x_1\}</math>, <math>\{x_2\}</math>, usw. sind alle in <math>\mathcal{V}</math>. Dann bilden wir die Vereinigung <math>\{x_1\} \cup \{x_2\}</math>, anschliessend <math>(\{x_1\} \cup \{x_2\}) \cup x_3</math> usw. ✔ }} Mit den bisher vorgestellten Axiomen können wir folgende Reihe von Mengen erzeugen: {{Formel|<math>\varnothing</math>, &nbsp; <math>\color{Cerulean}\{\varnothing\}</math>, &nbsp; <math>\{\varnothing, \color{Cerulean}\{\varnothing\} \color{Black}\}</math>, &nbsp; <math>\{\varnothing, \color{Cerulean}\{\varnothing\}, \color{Red}\{\varnothing,\{\varnothing\}\} \color{Black}\}</math>, &nbsp; <math>\{\varnothing, \color{Cerulean}\{\varnothing\}, \color{Red}\{\varnothing,\{\varnothing\}\}, \color{Green}\{\varnothing, \{\varnothing\}, \{\varnothing,\{\varnothing\}\}\} \color{Black}\}</math> &nbsp; <math>\dotsc</math>}} Die erste Menge hat '''0''' Elemente, die nächste '''1''', die nächste '''2''', die nächste '''3''', usw. Für die auf <math>x</math> folgende Menge <math>x'</math> sammeln wir alle Elemente von <math>x</math> ein und fügen zusätzlich <math>x</math> selbst dazu: <math>x' := x \cup \{ x \}</math>. Dieses Bildungsgesetz zeigt, dass die Reihe tatsächlich nur Mengen enthält: nach dem Axiom der leeren Menge gilt <math>\varnothing \in \mathcal{V}</math> und wenn <math>x \in \mathcal{V}</math> so folgt mit dem Paarmengenaxiom <math>\{ x \} \in \mathcal{V}</math>. Mit dem Vereinigungsmengenaxiom ergibt sich daraus <math>x \cup \{ x \} \in \mathcal{V}</math>. {{:Mathe für Nicht-Freaks: Vorlage:Definition |titel = Nachfolger |definition = Ist <math>\mathcal{K}</math> eine Klasse, dann ist <math>\mathcal{K}' := \mathcal{K} \cup \{\mathcal{K}\}</math>. }} Für eine Menge <math>x</math> ist <math>x'</math> ebenfalls eine Menge! Für echte Klassen bringt die Nachfolge nichts: {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Verständnisfrage |frage= Es gilt: <math>\mathcal{K} \notin \mathcal{V} \Rightarrow \mathcal{K}' = \mathcal{K}</math>. Warum? |antwort= Die Einerklasse von <math>\mathcal{K}</math> ist leer, wenn <math>\mathcal{K}</math> eine echte Klasse ist, denn <math>\{K\} = \{x \, | \, x = \mathcal{K} \}</math>. }} Mit dieser Nachfolger-Funktion lassen sich die '''natürlichen Zahlen''' definieren! Auf diese Idee war 1923 der ungarisch-amerikanische Mathematiker [[w:John von Neumann|John von Neumann]] gekommen. Er definierte: :{| class="wikitable" |- ! <math>\boldsymbol{0}</math> | colspan=3 | <math>:= \varnothing</math> | &nbsp; |- ! <math>\boldsymbol{1}</math> | <math>:= 0'</math> | <math>= 0 \cup \{0\}</math> | <math>= \color{Cerulean}\{\varnothing\}</math> | <math>= \{0\}</math> |- ! <math>\boldsymbol{2}</math> | <math>:= 1'</math> | <math>= 1 \cup \{1\}</math> | <math>= \{\varnothing, \color{Cerulean}\{\varnothing\} \color{Black}\}</math> | <math>= \{0,1\}</math> |- ! <math>\boldsymbol{3}</math> | <math>:= 2'</math> | <math>= 2 \cup \{2\}</math> | <math>= \{\varnothing, \color{Cerulean}\{\varnothing\}, \color{Red}\{\varnothing,\{\varnothing\}\} \color{Black}\}</math> | <math>= \{0,1,2\}</math> |- ! <math>\boldsymbol{4}</math> | <math>:= 3'</math> | <math>= 3 \cup \{3\}</math> | <math>= \{\varnothing, \color{Cerulean}\{\varnothing\}, \color{Red}\{\varnothing,\{\varnothing\}\}, \color{Green}\{\varnothing, \{\varnothing\}, \{\varnothing,\{\varnothing\}\}\} \color{Black}\}</math> | <math>= \{0,1,2,3\}</math> |- | colspan=5 align=center | usw. |} Auf diese Weise lässt sich ''jede einzelne'' natürliche Zahl definieren. == Aussonderung == Der [[:Mathe für Nicht-Freaks: Durchschnitt von Mengen|große Durchschnitt]] von <math>\mathcal{K}</math> wurde so definiert: <math>\bigcap \mathcal{K} := \{ x \, | \, \forall y \in \mathcal{K} : x \in y \}</math>. Hier werden die Elemente gesammelt, die in ''allen'' Elementen von <math>\mathcal{K}</math> legen. Der Durchschnitt einer Zweierklasse ist der Durchschnitt seiner beiden Elemente: <math>\bigcap \{x, y\} = x \cap y</math> {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Verständnisfrage |frage= Es gilt <math>\bigcap \varnothing = \mathcal{V}</math>. Warum? |antwort= Dazu müssen wir uns die Definition von <math>\bigcap \varnothing</math> genau anschauen: <math>\{ x \, | \, \forall y \in \varnothing : x \in y \}</math> steht für <math>\{ x \, | \, \forall y : y \in \varnothing \Rightarrow x \in y \}</math>. Die Prämisse von <math>\Rightarrow</math> ist immer falsch, daher ist die Implikation immer wahr, vgl. [[Mathe für Nicht-Freaks: Wahrheitstabelle|Wahrheitstabelle]] von <math>\Rightarrow</math>. Daher erfüllen ''alle'' <math>x</math> die definierende Bedingung für <math>\bigcap \varnothing</math>. ✔ <ref>Anmerkung: Manche Autoren betrachten <math>\bigcap \varnothing</math> als undefiniert oder setzen <math>\bigcap \varnothing := \varnothing</math>. Das liegt oftmals nur daran, dass die Allklasse <math>\mathcal{V}</math> vermieden werden soll. Einen zwingenden logischen Grund hierfür gibt es nicht.</ref> }} {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Verständnisfrage |frage= Es gilt <math>\forall x \in \mathcal{K} : \bigcap \mathcal{K} \subseteq x</math> für eine beliebige Klasse <math>\mathcal{K}</math>. Warum? |antwort= Ist <math>\mathcal{K} = \varnothing</math> ist nichts zu zeigen. Sei also <math>x \in \mathcal{K}</math>. Da nach der definierenden Bedingung von <math>\bigcap \mathcal{K}</math> die Elemente von <math>\bigcap \mathcal{K}</math> in ''allen'' Elementen von <math>\mathcal{K}</math> liegen müssen, so liegen sie insbesondere auch in <math>x</math>. ✔ }} {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Aufgabe |frage= Zeige: <math>\bigcap \mathcal{V} = \varnothing</math>. |antwort= Nach dem Axiom über die leere Menge gilt <math>\varnothing \in \mathcal{V}</math>. Daher müsen alle Elemente von <math>\bigcap \mathcal{V}</math> in <math>\varnothing</math> liegen. Das gilt aber für kein Element. Also ist <math>\bigcap \mathcal{V}</math> leer. ✔ }} Mit den bisherigen Axiomen können wir noch nicht zeigen, dass der Durchschnitt zweier Mengen wieder eine Menge ist. In der zweiten Verständnisfrage haben wir gesehen, dass der Durchschnitt einer nichtleeren Klasse immer eine Teilklasse einer Menge ist. Und es passt gut zu unsrer Vorstellung von kleinen Klassen, wenn solche Teilklassen ebenfalls Mengen sind: <span style="color:LightSeaGreen">'''Aussonderungsaxiom'''</span> {{Formel|<math>\begin{align} & \forall x : (\mathcal{K} \subseteq x \Rightarrow \mathcal{K} \in \mathcal{V}) \\ & \color{Black} \text{Die Teilklasse einer Menge ist eine Menge.} \end{align}</math>}} Das Axiom gilt für beliebige Klassen <math>\mathcal{K}</math>. Da es ja zu jeder Formel <math>\phi(x)</math> die Klasse <math>\{x \, | \, \phi(x) \}</math> gibt, wird dieses Axiom oft als ''Axiomenschema'' bezeichnet. {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Verständnisfrage |frage= Die Allklasse ist keine Menge sondern eine echte Klasse: <math>\mathcal{V} \notin \mathcal{V}</math>. Warum? |antwort= Die Russelsche Klasse <math>\mathcal{R}</math> ist eine Teilklasse von <math>\mathcal{V}</math>, also: <math>\mathcal{R} \subseteq \mathcal{V}</math>. Wäre <math>\mathcal{V}</math> eine Menge, so wäre mit der Aussonderung auch <math>\mathcal{R}</math> eine Menge. ↯ }} {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Aufgabe |frage= <math>A(x)</math> sei eine beliebige Aussageform, <math>\mathcal{M}</math> eine beliebige Klasse. Zeige: # <math>\forall x : \{ y \in x \, | \, A(y) \} \in \mathcal{V}</math> # <math>\forall x : \mathcal{M} \cap x \in \mathcal{V}</math> |antwort=Beweis: # Es gilt ja <math>\{ y \in x \, | \, A(y) \} \subseteq x</math> und mit <math>\mathcal{K} := \{ y \in x \, | \, A(y) \}</math> folgt mit der Aussonderung die Behauptung. ✔ # Ebenso ist <math>\mathcal{M} \cap x \subseteq x</math> und die Aussonderung liefert <math>\mathcal{M} \cap x \in \mathcal{V}</math>. ✔ }} Beide Formulierungen sind gleichwertig zum Aussonderungsaxiom. Denn mit der Definition <math>A(y) := y \in \mathcal{M}</math> erhalten wir aus 1. <math>\forall x : \{ y \in x \, | \, y \in \mathcal{M} \} \in \mathcal{V}</math>. Und weil <math>\{ y \in x \, | \, y \in \mathcal{M} \} = (\mathcal{M}\cap x)</math> ist, folgt daraus 2. <math>\forall x : \mathcal{M} \cap x \in \mathcal{V}</math>. Aus 2. folgt das Aussonderungsaxiom, denn wenn <math>\mathcal{K} \subseteq x</math> gilt, ist <math>\mathcal{K} = \mathcal{K} \cap x \in \mathcal{V}</math>. Zusammen mit der Aufgabe haben wir folgendes gezeigt: Aussonderungsaxiom <math>\Longrightarrow</math> 1. und 1. <math>\Longrightarrow</math> 2. und 2. <math>\Longrightarrow</math> Aussonderungsaxion. Das zeigt, das alle drei Aussagen äquivalent sind. Vgl. Kapitel [[:Mathe für Nicht-Freaks: Wahrheitstabelle|"Wahrheitstabelle"]]. {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Verständnisfrage |frage= Es gilt <math>\mathcal{K} \neq \varnothing \Rightarrow \bigcap \mathcal{K} \in \mathcal{V}</math>. Warum? |antwort= Ist <math>\mathcal{K} \neq \varnothing</math> gibt es ein <math>x \in \mathcal{K}</math> mit <math>\bigcap \mathcal{K} \subseteq x</math>. Mit der Aussonderung folgt daraus die Behauptung. ✔ }} {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Aufgabe |frage= <math>\mathcal{A}</math> und <math>\mathcal{B}</math> seien Klassen. Zeige: <math>\mathcal{A} \notin \mathcal{V} \land \mathcal{A} \subseteq \mathcal{B}\Rightarrow \mathcal{B} \notin \mathcal{V}</math> |antwort= Wäre <math>\mathcal{B} \in \mathcal{V}</math> so folgte wegen <math>\mathcal{A} \subseteq \mathcal{B}</math> und der Aussonderung <math>\mathcal{A} \in \mathcal{V}</math> ↯ }} Oberklassen von echten Klassen sind also ebenfalls echte Klassen und keine Mengen. Das entspricht unserer Vorstellung, dass Mengen kleine Klassen sind. {{:Mathe für Nicht-Freaks: Vorlage:Satz |titel=Hilfssatz |satz= Sei <math>\mathcal{A}</math> eine Menge. Dann gilt: <math>\exists x : x \notin \mathcal{A}</math>. |erklärung=Das heißt nichts anderes als: keine Menge enthält alle Mengen! |beweis= Wir definieren <math>\mathcal{M} := \{x \in \mathcal{A} \, | \, x \notin x \}</math>. Dann gilt <math>\mathcal{M} \subseteq \mathcal{A}</math>, also ist <math>\mathcal{M}</math> eine Menge. Mit dem Abstraktionsprinzip gilt <math>x \in \mathcal{M} \iff x \in \mathcal{A} \land x \notin x</math>. Weil <math>\mathcal{M}</math> eine Menge ist, dürfen wir <math>\mathcal{M}</math> für <math>x</math> einsetzen und erhalten: <math>\mathcal{M} \in \mathcal{M} \iff \mathcal{M} \in \mathcal{A} \land \mathcal{M} \notin \mathcal{M}</math>. Gälte <math>\mathcal{M} \in \mathcal{A}</math>, folgte <math>\mathcal{M} \in \mathcal{M} \iff \mathcal{M} \notin \mathcal{M}</math> ein Widerspruch. ↯ Also gilt: <math>\mathcal{M} \notin \mathcal{A}</math>. ✔ }} Mit <math>\mathcal{M}</math> haben wir eine Menge gefunden, die kein Element der Menge <math>\mathcal{A}</math> ist. Aus dem Hilfssatz ergibt sich, dass die Allklasse <math>\mathcal{V}</math> keine Menge, sondern eine echte Klasse ist. Denn wäre <math>\mathcal{V}</math> eine Menge, gäbe es eine Menge <math>\mathcal{M}</math>, die nicht in <math>\mathcal{V}</math> läge. ↯ Also gilt: {{:Mathe für Nicht-Freaks: Vorlage:Satz |titel=Allklasse |satz=Die Allklasse ist eine echte Klasse: <math>\mathcal{V} \notin \mathcal{V}</math> }} Anmerkung: Die Konstruktion von <math>\mathcal{M}</math> entspricht der [[Mathe für Nicht-Freaks: Russells Antinomie und Klassen|Russellschen Klasse]] <math>\mathcal{R}</math>. Aus dem Beweis des Hilfssatzes ergibt sich auch, dass <math>\mathcal{M} \notin \mathcal{M}</math> gilt. Ist <math>\mathcal{K}</math> eine Menge, so ist das Komplement <math>\mathcal{K}^\complement</math> eine echte Klasse. Auch hierin spiegelt sich unsere Vorstellung wider, dass Mengen "kleine" Klassen sind! {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Verständnisfrage |frage= Für beliebige Klassen <math>\mathcal{K}</math> gilt: <math>\mathcal{K} \in \mathcal{V} \Longrightarrow \mathcal{K}^\complement \notin\mathcal{V}</math>. Warum? |antwort= Wären <math>\mathcal{K}</math> und <math>\mathcal{K}^\complement</math> Mengen, so wäre auch die Vereinigungsmenge <math>\mathcal{K} \cup \mathcal{K}^\complement = \mathcal{V}</math> eine Menge. ↯ }} == Ersetzung == {{:Mathe für Nicht-Freaks: Vorlage:Warnung | Um das Folgende zu verstehen, werden ''Funktionen'' benötigt! Vgl. dazu das Kapitel [[Mathe für Nicht-Freaks: Abbildung, Funktion|"Abbildung, Funktion"]] }} Wir benutzen die folgenden Schreibweisen und Definitionen: {{:Mathe für Nicht-Freaks: Vorlage:Definition |titel=Schreibweisen und Eigenschaften von Funktionen |definition= * <math>f : D \longmapsto Z := f</math> ist eine Funktion von <math>D</math> in <math>Z</math> * <math>\mathsf{Fkt}(f) :\iff f</math> ist eine Funktion * <math>\mathsf{Db}(f) := \{ x \, | \; \exists y : f(x) = y \}</math> (Definitionsbereich von <math>f</math>) * <math>\mathsf{Wb}(f) := \{ y \, | \; \exists x : f(x) = y \}</math> (Wertebereich von <math>f</math>) * <math>f</math> ist injektiv <math>:\iff \forall x,y \in \mathsf{Db}(f) : f(x) = f(y) \Rightarrow x = y</math> * <math>f</math> ist surjektiv auf <math>B :\iff \forall y \in B \; \exists x \in \mathsf{Db}(f) : f(x) = y</math> * <math>f</math> ist bijektiv auf <math>B :\iff f</math> ist injektiv und surjektiv auf <math>B</math> }} <span style="color:LightSeaGreen">'''Ersetzungsaxiom'''</span> {{Formel|<math>\begin{align} & \mathsf{Fkt}(f) \land \mathsf{Db}(f) \in \mathcal{V} \Rightarrow \mathsf{Wb}(f) \in \mathcal{V} \\ & \color{Black} \text{Ist der Definitionsbereich einer Funktion eine Menge, so ist auch der Wertebereich dieser Funktion eine Menge.} \end{align}</math>}} Mit anderen Worten: ersetzt man die Elemente einer Menge durch andere Elemente, so erhält man wieder eine Menge. Auch dieses Axiom gilt für eine beliebige Klasse <math>f</math> und wird daher oft als ''Axiomenschema'' bezeichnet. Als Anwendung zeigen wir, dass die Vereinigung von mengenvielen Mengen wieder eine Menge ergibt. {{:Mathe für Nicht-Freaks: Vorlage:Satz |titel=Große Vereinigung |satz= <math>I \in \mathcal{V} \land \forall i \in I : A_i \in \mathcal{V} \Longrightarrow \bigcup_{i \in I} A_i \in \mathcal{V}</math> |erklärung= Das folgt noch nicht allein aus dem Vereinigungsmengen-Axiom! |beweis= Setzen wir <math>\mathcal{M} := \{ A_1, A_2, A_3, \dotsc , A_i, \dotsc \}</math> so gilt <math>\bigcup_{i \in I} A_i = \bigcup \mathcal{M}</math>. Die Behauptung folgt nun mit dem Vereinigungsmengen-Axiom, wenn <math>\mathcal{M}</math> eine Menge ist. Das ist aber nach dem Ersetzungs-Axiom der Fall. Dazu betrachten wir die Funktion <math>f: I \longmapsto \mathcal{M}</math> mit <math>f(i) := A_i</math>. Der Definitionsbereich <math>\mathsf{Db}(f) = I</math> ist eine Menge, also auch der Wertebereich <math>\mathsf{Wb}(f) = \mathcal{M}</math>. ✔ }} == Potenzmenge == Zur Erinnerung: <math>\mathcal{P}(\mathcal{K}) := \{ x \, | \, x \subseteq \mathcal{K} \}</math>. Wir können mit den bisherigen Axiomen nicht nachweisen, dass die Potenzklasse einer Menge wieder eine Menge ist! Daher fordern wir es in einem eigenen Axiom: <span style="color:LightSeaGreen">'''Potenzmengenaxiom'''</span> {{Formel|<math>\begin{align} & \forall x : \mathcal{P}(x) \in \mathcal{V} \\ & \color{Black} \text{Die Potenzklasse einer Menge ist ebenfalls eine Menge.} \end{align}</math>}} Wir wollen nun zeigen, dass die Bildung von Potenzmengen zu immer größeren Mengen führt. Unser Vorstellung von Mengen als kleine Klassen wird dabei etwas belastet, aber es ist uns wichtig, dass die Klasse aller Teilmengen einer Menge wieder eine Menge ist. Mit Hilfe von Funktionen können wir die Größe von Klassen vergleichen, ''ohne'' die Anzahl ihrer Elemente zu kennen. Diese Anzahl wird [[Mathe für Nicht-Freaks: Mächtigkeit von Mengen|Mächtigkeit]] genannt. Mit den bisher vorgestellten Axiomen können wir noch nicht genau sagen, was die Anzahl bei Klassen mit unendlich vielen Elementen ist! Wir beschränken uns daher auf den Vergleich von Klassen und nennen eine Klasse <math>\mathcal{A}</math> ''schmächtiger oder gleichmächtig'' zu <math>\mathcal{B}</math>, wenn es eine injektive Funktion gibt, die <math>\mathcal{A}</math> in <math>\mathcal{B}</math> abbildet. Ist <math>\mathcal{A}</math> schmächtiger als <math>\mathcal{B}</math>, so ist <math>\mathcal{B}</math> ''mächtiger'' als <math>\mathcal{A}</math>. {{:Mathe für Nicht-Freaks: Vorlage:Definition |titel=Mächtigkeit von Klassen |definition= <math>\mathcal{A}</math> und <math>\mathcal{B}</math> seien Klassen. * <math>\mathcal{A} \sim \mathcal{B} :\iff \exists f : \mathcal{A} \longmapsto \mathcal{B} \text{ bijektiv}</math> &nbsp; (<math>\mathcal{A}</math> ist gleichmächtig zu <math>\mathcal{B}</math>) * <math>\mathcal{A} \precsim \mathcal{B} :\iff \exists f : \mathcal{A} \longmapsto \mathcal{B} \text{ injektiv}</math> &nbsp; (<math>\mathcal{A}</math> ist schmächtiger als <math>\mathcal{B}</math>, &nbsp; <math>\mathcal{B}</math> ist mächtiger als <math>\mathcal{A}</math>) * <math>\mathcal{A} \precnsim \mathcal{B} :\iff \mathcal{A} \precsim \mathcal{B} \land \neg (\mathcal{A} \sim \mathcal{B})</math> &nbsp; (<math>\mathcal{A}</math> ist echt schmächtiger als <math>\mathcal{B}</math>, &nbsp; <math>\mathcal{B}</math> ist echt mächtiger als <math>\mathcal{A}</math>). }} Um zu zeigen, dass <math>\mathcal{A}</math> echt schmächtiger als <math>\mathcal{B}</math> ist, müssen also zwei Bedingungen gezeigt werden: # es gibt eine injektive Funktion von <math>\mathcal{A}</math> in <math>\mathcal{B}</math>, # es gibt keine bijektive Funktion von <math>\mathcal{A}</math> auf <math>\mathcal{B}</math>. Nach diesen Vorbereitungen können wir nun beweisen, dass eine Menge <math>\mathcal{A}</math> schmächtiger als ihre Potenzklasse <math>\mathcal{P}(\mathcal{A})</math> ist. {{:Mathe für Nicht-Freaks: Vorlage:Satz |titel=Satz von Cantor |satz= Sei <math>\mathcal{A}</math> eine Menge. Dann gilt: <math>\mathcal{A} \precnsim \mathcal{P}(\mathcal{A})</math>. |erklärung=Die Potenzmenge einer Menge ist stets mächtiger als die Menge selbst. |beweis= # Die Funktion, die jedem Element <math>x \in \mathcal{A}</math> die Einermenge <math>\{ x \}</math> zuordnet, ist injektiv und bildet <math>\mathcal{A}</math> in <math>\mathcal{P}(\mathcal{A})</math> ab. Also gilt: <math>\mathcal{A} \precsim \mathcal{P}(\mathcal{A})</math>. # Annahme: <math>f : \mathcal{A} \longmapsto \mathcal{P}(\mathcal{A})</math> bijektiv. Wir definieren die Klasse <math>B := \{ x \in \mathcal{A} \, | \, x \notin f(x) \}</math>. <math>B</math> ist eine Teilmenge von <math>\mathcal{A}</math> und daher gilt: <math>B \in \mathcal{P}(\mathcal{A})</math>. Wegen der Annahme dass <math>f</math> surjektiv ist, gibt es <math>b \in \mathcal{A}</math> mit <math>f(b) = B</math>. Nach Definition von <math>B</math> folgt: <math>b \in B \iff b \in \mathcal{A} \land b \notin f(b)</math> und daraus: <math>b \in f(b) \iff b \notin f(b)</math>. ↯ Also gibt es keine bijektive Funktion und es gilt: <math>\mathcal{A} \precnsim \mathcal{P}(\mathcal{A})</math>. ✔ }} == Unendlichkeit == Mit etwas Aufwand<ref>Siehe z.B. Arnold Oberschelp: ''Allgemeine Mengenlehre.'' 1994</ref> – den wir hier nicht darstellen wollen – lässt sich die ''Klasse'' <math>\N = \{0, 1, 2, 3, \dotsc \}</math> aller natürlichen Zahlen definieren. Dazu reichen die bisher vorgestellten Axiome völlig aus. Wir nutzen hier zur Definition von <math>\N</math> die Eigenschaft, dass sich die natürlichen Zahlen schrittweise als Nachfolger erzeugen lassen. Klassen, die die leere Menge enthalten und mit jedem Element auch den Nachfolger dieses Elements, heißen ''induktiv'': {{:Mathe für Nicht-Freaks: Vorlage:Definition |titel = Induktive Klassen |definition = <math>\mathcal{K}</math> ist '''induktiv''' <math>:\Longleftrightarrow \varnothing \in \mathcal{K} \land \forall x \in \mathcal{K} : x' \in K</math>. }} Nach dieser Definition sind die natürlichen Zahlen offensichtlich in ''allen'' induktiven Klassen enthalten. Wir definieren daher: {{:Mathe für Nicht-Freaks: Vorlage:Definition |titel = Natürliche Zahlen |definition = <math>\N := \bigcap \{x \, | \, x \, ist \, induktiv \}</math>. }} Achtung! Diese Definition liefert nur dann das Gewünschte, wenn <math>\{x \, | \, x \, ist \, induktiv \}</math> nicht leer ist. Gibt es keine induktiven Mengen, geht der Durchschnitt über die leere Menge und liefert <math>\mathcal{V}</math>, wie im Abschnitt [[#Aussonderung|Aussonderung]] gezeigt. Es muss also eine induktive ''Menge'' geben, damit die Definition von <math>\N</math> in Ordnung ist. Genau das ist der Inhalt des ''Unendlichkeitsaxioms'': <span style="color:LightSeaGreen">'''Unendlichkeitssaxiom'''</span> {{Formel|<math>\begin{align} & \exist x : (\varnothing \in x \land \forall y \in x : y' \in x) \\ & \color{Black} \text{Es gibt eine Menge, die } \varnothing \text{ und alle Nachfolger enthält!} \end{align}</math>}} Mit dem Aussonderungsaxiom folgt direkt: {{:Mathe für Nicht-Freaks: Vorlage:Satz |titel=Natürliche Zahlen |satz= {{Formel|<math>\N \in \mathcal{V}</math>. &nbsp; '''Die Klasse der natürlichen Zahlen ist eine Menge.'''}} }} Da der Durchschnitt in allen induktiven Mengen enthalten ist, ist <math>\N</math> bezogen auf <math>\subseteq</math> die kleinste induktive Menge. Die natürlichen Zahlen werden durch die fünf [[w:Peano-Axiome|''Peano-Axiome'']] beschrieben. Diese werden wir nun nachweisen: {{:Mathe für Nicht-Freaks: Vorlage:Satz |titel=Natürliche Zahlen |satz=<math>\N</math> erfüllt die Peano-Axiome: # <math>0 \in \N</math> &nbsp; (0 ist eine natürliche Zahl.) # <math>\forall x \in \N : x' \in \N</math> &nbsp; (Jede natürliche Zahl hat eine natürliche Zahl als Nachfxolger.) # <math>\forall x \in \N : x' \ne 0</math> &nbsp; (0 ist kein Nachfolger einer natürlichen Zahl.) # <math>\forall x \in \N \; \forall y \in \N : x' = y' \Rightarrow x = y</math> &nbsp; (Natürliche Zahlen mit gleichem Nachfolger sind gleich.) # <math>\mathcal{K}</math> sei eine beliebige Klasse:<br><math>0 \in \mathcal{K} \land (\forall x \in \N : x \in \mathcal{K} \Rightarrow x' \in \mathcal{K}) \Rightarrow \N \subseteq \mathcal{K}</math><br>(Ist 0 aus <math>\mathcal{K}</math> und gilt für jede natürliche Zahl, wenn sie in <math>\mathcal{K}</math> ist, dann ist auch ihr Nachfolger in <math>\mathcal{K}</math>, dann sind alle natürlichen Zahlen in <math>\mathcal{K}</math>) |erklärung=Wir zeigen Punkt 4. zum Schluss. |beweis= # <math>0</math> (<math>:= \varnothing</math>) ist in jeder induktiven Menge enthalten, also auch im Durchschnitt <math>\N</math>. ✔ # Sei <math>x \in \N</math> nach Voraussetzung. Dann ist <math>x</math> und damit auch <math>x'</math> in allen induktiven Mengen enthalten, also auch in <math>\N</math>. ✔ # Der Nachfolger von x ist nicht leer! ✔ # Mit 5. steht das Beweisverfahren durch [[Mathe für Nicht-Freaks: Vollständige Induktion|vollständige Induktion]] zur Verfügung!<br> Wir zeigen zunächst einen Hilfsatz: <math>\forall x \in \N \; \forall y \in \N : y \in x' \Rightarrow y \subseteq x</math>. Induktion über <math>x</math>.<br>'''Induktionsanfang:''' Sei <math>x = 0</math> und <math>y \in 0' = \{ \varnothing \}</math>. Dann ist <math>y = \varnothing \subseteq \varnothing</math>.<br>'''Induktionsschluss:''' Gelte nun <math>y \in x' \Rightarrow y \subseteq x</math> und sei <math>y \in x'' = x' \cup \{x'\}</math>.<br>Dann ist <math>y \in x'</math> oder <math>y \in \{x'\}</math>.<br>Im erste Fall folgt mit der Induktionsvoraussetzung <math>y \subseteq x</math> und wegen <math>x \subseteq x'</math> auch <math>y \subseteq x'</math>.<br>Im zweiten Fall gilt <math>y = x'</math> und somit ebenfalls <math>y \subseteq x'</math>.<br>Damit ist der Hilfsatz bewiesen.<br>Sei nun <math>x, y \in \N</math> und gelte <math>x' = y'</math>.<br>Das heißt <math>x' = x \cup \{x\} = y \cup \{y\} = y'</math>.<br>Dann gilt <math>x \in y'</math> und mit dem Hilfssatz folgt <math>x \subseteq y</math>.<br>Ebenso folgt <math>y \in x'</math> und somit <math>y \subseteq x</math>.<br>Das zeigt <math>x = y</math>. ✔ # Wir konstruieren eine induktive Menge <math>\mathcal{M}</math> und zeigen, dass <math>\N \subseteq \mathcal{M} \subseteq \mathcal{K}</math> gilt. Daraus folgt <math>\N \subseteq \mathcal{K}</math>.<br>Sei <math>\mathcal{M} := \N \cap \mathcal{K}</math> und gelten die Voraussetzungen.<br>Dann ist <math>\mathcal{M}</math> eine Menge und es gilt <math>\mathcal{M} \subseteq \mathcal{K}</math>.<br>Es gilt <math>0 \in \mathcal{K}</math>, also ist <math>0 \in \mathcal{M}</math>.<br>Sei nun <math>x \in \mathcal{M}</math>, also <math>x \in \N</math> und <math>x \in \mathcal{K}</math>.<br>Dann ist <math>x' \in \N</math> (nach 2.) und <math>x' \in \mathcal{K}</math> (nach Voraussetzung).<br>Daher ist <math>x' \in \mathcal{M}</math> und somit <math>\mathcal{M}</math> induktiv.<br>Nach Definition von <math>\N</math> gilt daher <math>\N \subseteq \mathcal{M}</math>. ✔ }} {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Aufgabe |frage= Zeige dass jede natürliche Zahl selbst aus natürlichen Zahlen besteht: <math>\forall x \in \N : x \subseteq \N</math> |antwort=Induktion über <math>x</math>:<br>Induktionsanfang: <math>0 \subseteq \N</math>.<br>Induktionsschluss: Gelte nun <math>x \subseteq \N</math>. Dann ist <math>x' = x \cup \{x\} \subseteq \N</math>, denn die Vereinigung zweier Teilmengen von <math>\N</math> ist ebenfalls eine Teilmenge von <math>\N</math>. ✔ }} Anmerkung: Das 5. Peano-Axiom wird häufig mit Aussagenformen anstelle von Klassen formuliert: : <math>A(x)</math> sei eine beliebige Aussageform: : <math>A(0) \land (\forall x \in \N : A(x) \Rightarrow A(x')) \Rightarrow \forall x \in \N : A(x)</math> Wegen <math>\mathcal{K} = \{ x \, | \, A(x) \}</math> sind mit <math>A(x) :\Longleftrightarrow x \in \mathcal{K}</math> beide Formulierungen gleichwertig. == Fundierung == Mengen können – im Gegensatz zu echten Klassen – als Elemente auftreten. Dabei kann es zu Merkwürdigkeiten kommen, beispielsweise dass <math>x \in x</math> gilt. Wir wollen das näher untersuchen, dabei ist die folgende Definition hilfreich: {{:Mathe für Nicht-Freaks: Vorlage:Definition |titel = <math>\in</math>-minimal |definition = <math>x</math> ist <math>\boldsymbol{\in}</math>'''-minimal''' in <math>\mathcal{K}</math> genau dann, wenn <math>x \in \mathcal{K} \land \mathcal{K} \cap x = \varnothing</math> }} Wenn also ein <math>\in</math>-minimales Element in <math>\mathcal{K}</math> überhaupt Elemente hat, dann liegen sie nicht in <math>\mathcal{K}</math>. Das folgende Axiom fordert, dass jede nichtleere Menge <math>\in</math>-minimale Elemente hat: <span style="color:LightSeaGreen">'''Fundierungsaxiom'''</span> {{Formel|<math>\begin{align} & \forall x : (x \ne \varnothing \Rightarrow \exists y \in x : y \cap x = \varnothing) \\ & \color{Black} \text{Jede nichtleere Menge hat } \in\text{-minimale Elemente.} \end{align}</math>}} Mit diesem Axiom werden keine weiteren Mengen erzeugt, ganz im Gegenteil: Damit werden Folgen wie &nbsp; <math>x \in x</math>, &nbsp; <math>x \in y \in x</math>, &nbsp; <math>x \in y \in z \in x</math>, &nbsp; usw. ausgeschlossen. Es gibt also keine unendlich lang absteigende <math>\in</math>-Ketten. Daher kommt auch die Bezeichnung "Fundierung". {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Satz |frage= <math>\forall x_1, x_2, \dotsc, x_n : \neg (x_1 \in x_2 \in \dotsm \in x_n \in x_1)</math> |antwort= Angenommen es gäbe eine solche Kette. Dann bilden wir die Klasse <math>\mathcal{M} := \{x_1, x_2, \dotsc, x_n\}</math>.<br> Es ist <math>\mathcal{M} \in \mathcal{V}</math> und <math>\mathcal{M} \ne \varnothing</math>. Aber <math>\mathcal{M}</math> hat kein <math>\in</math>-minimales Element. ↯ }} {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Verständnisfrage |frage= Gibt es eine Menge <math>x</math> mit: <math>x = \mathcal{P}(x)</math> ? |antwort= Nein. Es gilt ja insbesondere <math>x \in \mathcal{P}(x)</math>. Wäre <math>x = \mathcal{P}(x)</math>, so folgte <math>x \in x</math> im Widerspruch zur Fundierung. ↯ }} {{:Mathe für Nicht-Freaks: Vorlage:Frage |typ=Verständnisfrage |frage= Was besagt die Fundierung für die Russelsche Klasse <math>\mathcal{R}</math>? |antwort= Da alle Mengen fundiert sind, gilt <math>\forall x : x \notin x</math> und die Russelsche Klasse ist die Allklasse: <math>\mathcal{R} = \{ x \, | \, x \notin x \} = \{ x \, | \, x = x \} = \mathcal{V}</math> }} Dieses Axiom hat rein praktische Gründe: auf dieses Weise werden unbequeme Mengen ausgeschlossen und die Mengenlehre wird einfacher. Die Mathematik kommt im Wesentlichen mit fundierten Mengen aus. == Auswahl == {{:Mathe für Nicht-Freaks: Vorlage:Warnung | Um das letzte Axiom zu verstehen, werden Kenntnisse über ''Partitionen (Zerlegungen)'' benötigt! Vgl. dazu das Kapitel [[Mathe für Nicht-Freaks: Äquivalenzrelation|"Äquivalenzrelationen"]] }} <span style="color:LightSeaGreen">'''Auswahlaxiom'''</span> {{Formel|<math>\begin{align} & \forall x : (\forall y \in x : y \ne \varnothing \land \forall y,z \in x : (y \ne z \Rightarrow y \cap z = \varnothing) \Rightarrow \exist w \; \forall y \in x \exist! z \in w : z \in y) \\ & \color{Black} \text{Zu jeder nichtleeren Menge von paarweise disjunkten Mengen gibt es eine Auswahlmenge.} \end{align}</math>}} Wir lesen dieses Axiom schrittweise: <math>x</math> sei eine beliebige Menge.<br> Die Prämisse besagt: <math>\underbrace{\quad \forall y \in x : \quad}_{die \; Elemente \; von \; x}</math> &nbsp; <math>\underbrace{\qquad y \ne \varnothing \qquad}_{sind \; nicht \; leer}</math> &nbsp; <math>\underbrace{\quad \land \quad \color{white}|}_{und}</math> &nbsp; <math>\underbrace{\forall y,z \in x : (y \ne z \Rightarrow y \cap z = \varnothing)}_{paarweise \; disjunkt}</math> Die Konklusion besagt: <math>\underbrace{\qquad \qquad \exist w \qquad \qquad \color{white}|}_{es \; gibt \; eine \; Menge \; w, \; die}</math> &nbsp; <math>\underbrace{\qquad \forall y \in x \qquad}_{aus\; jedem \; Element \; von \; x}</math> &nbsp; <math>\underbrace{\qquad \exist! z \in w : z \in y \qquad}_{genau \; ein \; Element \; enthalt}</math> Insgesamt heißt das also: Zu einer Menge <math>x</math> deren Elemente nicht leer und paarweise disjunkt sind, gibt es eine Menge <math>w</math>, die aus jedem Element von <math>x</math> genau ein Element enthält, also ''auswählt''. Die Menge <math>x</math> ist eine ''Partition'' der Menge <math>\bigcup x</math> und die Menge <math>w</math> wählt aus jedem Teil der Partition einen Repräsentanten aus: [[File:Partition mit Auswahl.png|zentriert|400px|Partition (links) mit Auswahlmenge (rechts)]] Das [[w:Auswahlaxiom|Auswahlaxiom]] sieht harmlos aus, hat aber erhebliche Auswirkungen. Es gibt eine Reihe von wichtigen Sätzen, die auf der Basis der übrigen Axiome zum Auswahlaxiom gleichwertig sind<ref>Siehe Wikipediaartikel [[w:Auswahlaxiom|Auswahlaxiom]]</ref>, unter anderem die folgenden: * '''Wohlordnungssatz:''' Jede Menge lässt sich wohlordnen. * '''Zornsches Lemma:''' Ist jede Kette einer halbgeordneten Menge nach oben beschränkt, hat die Menge ein maximales Element. Wir wollen hier aber die Vorstellung der Axiome von ZFC beenden. {{#invoke:Mathe für Nicht-Freaks/Seite|unten}} cu3wqpw77wkhggvifukb0kxfccwjna7 Wikibooks:GUS2Wiki 4 116489 1092067 1091880 2026-10-10T22:04:24Z Alexis Jazz 96587 Updating gadget usage statistics from [[Special:GadgetUsage]] ([[phab:T121049]]) 1092067 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-10-10, 05:56:18Z 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 || 3 |- |HotCat || 27 || 1 |- |Pfeil-hoch || 79 || 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 || 5 |- |wikEd || 175 || 3 |} * [[Spezial:GadgetUsage]] * [[m:Meta:GUS2Wiki/Script|GUS2Wiki]] <!-- data in CSV format: CollapseAll,31,3 HotCat,27,1 Pfeil-hoch,79,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,5 wikEd,175,3 --> b8s40eiubgw4rpiq9kb4q73ne2vj60d Ing Mathematik: Python 0 117969 1092057 1091907 2026-10-10T13:06:11Z Intruder 1513 1092057 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 (welche Verzeichnisse im PATH liegen, kann bspw. mit <code>echo $PATH</code> abgefragt werden). 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 == === Tests === 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) === Popularität === * An Beliebtheit rangiert Python mit Stand September 2026 mit einem Rating von 17,76% 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 Q1/2026 auf Rang 2, vor TypeScript und hinter JavaScript. * [https://pypl.github.io/PYPL.html PYPL] (Popularity of Programming Language Index) ist ein anderer Popularitätsindex für Programmiersprachen. Auch hier liegt Python mit großem Vorsprung auf dem ersten Platz (52,08%), gefolgt von Java (13,34%) und C/C++ (7,99%) (Stand September 2026). === Namensgebung === 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 in PyConUS24.jpg|{{W|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 <code>help</code> oder <code>help()</code> 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, bzw. der Eingabe von q, quit oder exit mit anschließendem Drücken der RETURN-Taste. == 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 Theoretisch kann man auch Umlaute, griechische Buchstaben u.ä. in Variablenbezeichnern nutzen, z.B. ößÜ = 16 α = 134 print(ößÜ) print(α) Ausgabe: 16 134 Davon wird aber aus offensichtlichen Gründen ({{W|Englisch als Lingua franca}}) abgeraten. Das Eurozeichen (€) etc. darf man ohnehin nicht in Variablenbezeichnern verwenden, dies ergäbe einen Syntaxfehler. Siehe dazu auch [https://peps.python.org/pep-3131/]. 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. Man beachte, dass {{W|Rundungsfehler}} auftreten können. D.h. man sollte bei der Verwendung von {{W|Gleitkommazahl|Gleitpunktzahlen}} niemals auf absolute Genauigkeit prüfen, sondern eine Toleranz (einen <math>\varepsilon</math>-Wert) vorgeben. Ein klassisches Beispiel, das ein falsches Ergebnis liefert, ist: i = 0.1 + 0.2 print(i) if i == 0.3: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 NOK Und das, obwohl man schon in der Volksschule lernt, dass 0.1 + 0.2 gleich 0.3 ist. Korrekt muss das obige Beispiel z.B. so umgeschrieben werden: ist = 0.1 + 0.2 soll = 0.3 epsilon = 0.000001 print(ist) if abs(ist-soll) < epsilon: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 OK Alternativ kann auch die <code>math.isclose()</code>-Funktion verwendet werden. Das sei aber hier nur erwähnt und nicht konkret ausgeführt. <u>Aufgabe:</u> Recherchieren Sie im Internet zum Thema <code>math.isclose()</code>-Funktion. === 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 == Kopfgesteuerte Schleife, Endlosschleife und fußgesteuerte Schleife: [[File:Programmingloops.svg|450px]] === 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 Der <code>break</code>-Befehl wird etwas später noch einmal kurz thematisiert. === 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!") print("Setze Programm fort!") Ausgabe: Das Ergebnis ist unendlich Setze Programm fort! 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). Danach wird das Programm fortgesetzt. 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. Neu in Python 3.14 ist, dass mehrere Ausnahmen auch ohne Klammern angegeben werden können, z.B.: bis inkl. Python 3.13 nur: except (ZeroDivisionError, ValueError): ab Python 3.14 auch: except ZeroDivisionError, ValueError: == 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 c, d = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(c) print(d) Ausgabe: 8 15 Es werden r1 und r2 zurückgegeben und den Variablen c und d zugewiesen. (r1, r2) und (c, d) sind Tupel. Was Tupel konkret sind, siehe etwas später in diesem Kapitel. 3 und 5 sind die aktuellen Parameter (die Argumente). a und b sind die formalen Parameter. Die Zuweisung der aktuellen zu den formalen Parametern erfolgt in der gegebenen Reihenfolge, also die Zahl 3 wird an a übergeben und die Zahl 5 an b. Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 c, d = mathFunc(3, 5) print(c) print(d) e, f = mathFunc(5) print(e) print(f) g, h = mathFunc(b=6) print(g) print(h) Ausgabe: 8 15 25 100 16 60 Erklärung: * Erster <code>mathFunc</code>-Aufruf: Es werden, wie weiter oben, die Argumente 3 und 5 an die formalen Parameter a und b übergeben. Die Rückgabewerte r1 und r2 werden an c und d übergeben. Somit ist c=8 und d=15. * Zweiter <code>mathFunc</code>-Aufruf: Es wird nur das Argment 5 an a übergeben. b behält den Vorgabewert 20. D.h. e= 25 und f=100. * Dritter <code>mathFunc</code>-Aufruf: Es wird nur die Zahl 6 an b übergeben, a behält seinen Vorgabewert 10. Somit ergibt sich g=16 und h=60. Beispiel (Schlüsselwortargumente vs. Positionsargumente): def mathFunc(a, b): r1 = a - b r2 = a / b return r1, r2 c, d = mathFunc(b=3, a=5) # Schlüsselwortargumente e, f = mathFunc(3, 5) # Positionsargumente print(c) print(d) print(e) print(f) Ausgabe: 2 1.6666666666666667 -2 0.6 Durch die Verwendung von Schlüsselwortargumenten können die Argumente in beliebiger Reihenfolge angegeben werden. Bei den Positionsargumenten ist die Reihenfolge vorgegeben. Daraus resultiert auch der Unterschied bei den obigen Ausgaben. Werden beide Arten in einem Aufruf gemischt, so müssen die Positionsargumente zuerst angegeben werden. Den Unterschied zwischen beiden Argumentarten zu kennen ist wichtig, nicht nur bei selbst erstellten Funktionen. Auch beim Aufruf von Python-Funktionen verfolgt einen die Mischung von Schlüsselwort- und Positionsargumenten auf Schritt und Tritt, z.B. beim etwas später folgenden Abschnitt über "Grafiken zeichnen": plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") Bei dieser <code>plot</code>-Funktion aus dem Modul <code>matplotlib.pyplot</code> sind <code>x</code> und <code>y1</code> Positionsargumente. <code>label, lw</code> und <code>ls</code> sind die Namen der Schlüsselwortargumente. Die Schlüsselwortargumente könnten auch in anderer Reihenfolge angeschrieben werden, nicht jedoch die Positionsargumente! Siehe zu <code>matplotlib.pyplot.plot</code> auch die Seite [https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html]. == 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 und Listen == 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. 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. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. # 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 (eine Liste von Wörtern zu einem einzigen zusammenhängenden Text verbinden): woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt == Dictionaries und Mengen == 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'> Leere Mengen lassen sich mit <code>set()</code> erzeugen: menge = set() # leere Menge print(menge) menge.add("Hallo") # Element hinzufügen print(menge) Ausgabe: set() {'Hallo'} Beispiel (einige Mengenoperationen): mengeA = {"Hallo", "Welt"} mengeB = {1, 2, "Welt", 3} # Vereinigung print(mengeA | mengeB) print(mengeA.union(mengeB)) # Durchschnitt print(mengeA & mengeB) print(mengeA.intersection(mengeB)) # Differenz print(mengeA - mengeB) print(mengeA.difference(mengeB)) # Symmetrische Differenz print(mengeA ^ mengeB) print(mengeA.symmetric_difference(mengeB)) # Teilmenge print(mengeA.issubset(mengeB)) # Mehrere Elemente hinzu mengeA.update(["Super", 67, 125]) print(mengeA) # Element löschen mengeA.discard("Hallo") print(mengeA) Ausgabe: {1, 2, 3, 'Welt', 'Hallo'} {1, 2, 3, 'Welt', 'Hallo'} {'Welt'} {'Welt'} {'Hallo'} {'Hallo'} {1, 2, 3, 'Hallo'} {1, 2, 3, 'Hallo'} False {67, 'Super', 'Welt', 'Hallo', 125} {67, 'Super', 'Welt', 125} Es gibt schon seit "ewigen" Zeiten unveränderbare Mengen, die mittels <code>frozenset</code> eingeleitet werden. Neu ab Python 3.15 ist dagegen das Pendant <code>frozendict</code>, das unveränderbare Dictionaries darstellt. fdict = frozendict({"vorname":"Hugo", "nachname":"Meister"}) fset = frozenset({"A", 1, "A"}) print(fdict) print(fdict["vorname"]) print (fset) try: fdict["vorname"] = "Max" print(fdict["vorname"]) except TypeError as e: print(f"Fehler: {e}") try: fset.add("B") print(fset) except AttributeError as e: print(f"Fehler: {e}") Ausgabe: frozendict({'vorname': 'Hugo', 'nachname': 'Meister'}) Hugo frozenset({1, 'A'}) Fehler: 'frozendict' object does not support item assignment Fehler: 'frozenset' object has no attribute 'add' == 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: x = ("a", "b", "c", "d", "e") print(x[2]) print(x[-3]) print(x) print(x[:]) print(x[2:]) print(x[:2]) print(x[::-1]) Ausgabe: c c ('a', 'b', 'c', 'd', 'e') ('a', 'b', 'c', 'd', 'e') ('c', 'd', 'e') ('a', 'b') ('e', 'd', 'c', 'b', 'a') Visualisierung: '''a''' '''b''' '''c''' '''d''' '''e''' +-----------------------------+ | 0 | 1 |//'''2'''//| 3 | 4 | +-----------------------------+ | -5 | -4 |/'''-3'''//| -2 | -1 | +-----------------------------+ |/////////////////////////////| +-----------------------------+ |/////////////''':'''///////////////| +-----------------------------+ | | |///////'''2:'''////////| +-----------------------------+ |////''':2'''/////| | | | +-----------------------------+ |\\\\\<--\\\\\'''::-1'''\\\\<--\\\\\| +-----------------------------+ Beispiel: x = ("a", "b", "c", "d", "e") print(x[2::2]) print(x[2::-1]) print(x[2:0:-1]) Ausgabe: ('c', 'e') ('c', 'b', 'a') ('c', 'b') Hier sieht man sehr schön, dass beim letzen <code>print</code>-Befehl die 0, die in diesem Fall den Buchstaben "a" repräsentiert, als letzer Wert nicht mehr dazugehört (wie weiter oben angedeutet ist der End- oder Stoppwert exklusiv). Will man den Buchstaben "a" dazunehmen, so ist die vorletzte Zeile die richtige. Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH Mit zweidimensionalen Listen/Tupels wird das Ganze schon bedeutend komplexer. Beispiel: Verkettetes Slicing A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] print(A[2::][::2]) Ausgabe: [['20', '21', '22', '23'], ['40', '41', '42', '43']] Das erste Slicing erstellt folgenden Slice: [["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] Das zweite Slicing wird dann auf diesen Slice angewendet und ergibt das genannte Endergebnis. Will man Spalten statt Zeilen, so funktioniert dieser direkte Weg nicht. Man verwendet dann List Comprehensions oder die NumPy-Bibliothek. Diese Bibliothek erlaubt zweidimensionales Slicing. Sie wird erst später bei den Grafiken eingeführt. Deshalb wird hier dazu noch kein Beispiel gezeigt. = 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 (ist der sogenannte Konstruktor). 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 strikt/stark private ist. Wird nur ein Unterstrich verwendet, so deutet dies auf schwach/weakly private hin. Letzteres wird in Python deutlich häufiger verwendet. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. Besonderheiten bei stark und schwach private: * '''stark private''': ** zwei führende Unterstriche ** Name Mangling: Name wird zu <code>_Klassenname__variablenname</code> ** Schutz vor Namenskollisionen bei Vererbung ** am Ende des Namens dürfen keine zwei Unterstriche verwendet werden. Das wären dann Magic Methods oder Dunder-Methods (z.B. __init__). Diese sind public. * '''schwach private''': ** ein führender Unterstrich ** Name bleibt gleich ** ist nur ein Hinweis an die Programmierer, kein starker Schutz ** <code>from modul import *</code>: Objekte mit einem führenden Unterstrich werden standardmäßig nicht importiert ** Standard für die Kapselung bei Python. Schätzung: Weit mehr als 80% der nicht-öffentlichen Attribute/Methoden werden als schwach private deklariert. Weniger als 5-10% als stark private (Quelle: Google KI) == 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 == === Einfachvererbung === [[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 === Mehrfachvererbung === Mit Python ist auch Mehrfachvererbung möglich, z.B.: class A: def _printA(self): print("Klasse A") class B: def _printB(self): print("Klasse B") class C(A,B): def __init__(self): self._printA() self._printB() c = C() Ausgabe: Klasse A Klasse B Die Klasse C erbt von A und B. In UML sieht das in etwa so aus: +-----------------------------+ +-------------------------+ | A | | B | +-----------------------------+ +-------------------------+ | | | | +-----------------------------+ +-------------------------+ | # printA() | | # printB() | +-----------------------------+ +-------------------------+ . . / \ / \ --- --- | | | | +---------------------+ | C | +---------------------+ | | +---------------------+ | | +---------------------+ <code>printA()</code> und <code>printB()</code> sind schwach private (in UML als <code>protected</code> mit einem # dargestellt). Es ist aber zu beachten, dass schwach private nur Konvention ist. Praktisch könnte man auch von außerhalb auf diese Methoden zugreifen. Das soll man aber nicht tun, und das wird in Python durch einen Unterstrich angedeutet. == Statische Attribute und Methoden == Beispiel: class Math: pi = 3.14159265 cnt = 0 @staticmethod def expo(): return 2.71828183 @classmethod def counterPlus(cls): cls.cnt += 1 print(Math.pi) print(Math.expo()) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Ausgabe: 3.14159265 2.71828183 1 2 3 Statische Attribute und Methoden, bzw. Klassenmethoden, sind direkt einer Klasse zugeordnet und nicht einem Objekt. Der Unterschied zwischen statischen Methoden und Klassenmethoden ist, dass statische Methoden nicht auf Klassenattribute (bzw. statische Attribute) zugreifen können, Klassenmethoden aber schon. = 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. Die "arange"-Funktion aus der NumPy-Bibliothek wird später ein bisschen genauer behandelt (siehe [[Ing_Mathematik:_Python#Zahlenfolgen|Zahlenfolgen]]). <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. Die Farbe des Graphen kann man über den <code>color</code>-Schalter einstellen, z.B. auf rot setzen: plt.plot(x, y, color="red") Das ist nur eine Möglichkeit, es gibt auch andere, z.B. mittels * hexadezimaler Zahl, z.B. <code>color="#ff0000"</code> * Kurzform, z.B. <code>"r"</code> Statt <code>color</code> kann auch die Kurzform <code>c</code> verwendet werden. <u>Kleine Aufgabe:</u> Probieren Sie diese "Farbspielereien" am Computer aus. === Graphen mehrerer Funktionen und weiteres === ==== Mehrere Funktionen in einen Plot zeichnen ==== 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]] Einige weitere Bespiele zu <code>ls</code></code>: * - ... durchgezogene Linie * -. ... Strichpunkt-Linie * dashed ... gestrichelte Linie ==== Marker ==== Desweiteren kann man Marker verwenden. Beispiel: import matplotlib.pyplot as plt plt.plot(10, 10, "ob", markersize=30) plt.plot(20,20, ".r") plt.plot(30,30, "Dg", ms=15) plt.plot(40, 40, "ok", ms=20, mec="#FF5733", mew=3) plt.plot(50, 50, "vk", ms=20, mec="yellow", mew=3) plt.grid() plt.show() Das erste Zeichen im String nach den Koordinaten im plot-Befehl symbolisiert die Form (z.B. o für einen Kreis). Das zweite Zeichen steht für die Farbe (z.B. b für blue). <code>markersize</code> oder <code>ms</code> setzt die Größe des Markers. Ausgabe: [[Bild:PythonIng_formatpara1.svg|200px]] Einige weitere Marker: * * ... Stern * + ... Plus * h oder H ... Sechseck Man kann mit den Parametern * <code>markeredgecolor</code> oder kurz <code>mec</code> die Farbe des Markerrandes * <code>markeredgewidth</code> oder kurz <code>mew</code> die Dicke des Randes * <code>markerfacecolor</code> oder kurz <code>mfc</code> die Innenfarbe des Markers bestimmen. === 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]] === Anmerkungen hinzufügen === Anmerkungen (engl. annotations) lassen sich mit matplotlib einfach hinzufügen. Unter Anmerkung wird hier ein Pfeil mit Text verstanden. Beispiel: import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot([1,10], [1,10]) ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0)) plt.show() Ausgabe: [[Datei: PythonIng_ann1.svg|400px]] <code>shrink</code> verkürzt den Pfeil. Sinnvoll sind beim obigen Beispiel Werte irgendwo zwischen 0.0 und 0.3. <u>Kleine Aufgabe:</u> Probieren Sie verschiedene Pfeillängen aus. Die Schriftgröße verändern können Sie übrigens mit <code>fontsize</code> oder <code>size</code>, z.B. ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0), fontsize=25) Eine andere Schreibweise für Dictionaries, die wir weiter oben schon kennengelernt haben, die wir aber nochmals explizit aufschreiben wollen, ist ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops={"facecolor":"red", "shrink":0.0}) === Figure, Axes etc.=== [[Bild:PythonIng_figure33.svg|350px]] * fig (figure) ... das übergeordnete Fenster * ax (axes) ... "Plot", das eigentliche Zeichenfenster * axis ... die Koordinatenachsen * spine ... Begrenzungslinien des Zeichenfensters * tick ... Markierung an den Achsen des Zeichenfensters Beispiel (eine figure mit 6 axes, ax-Titel, y-axis-Titel): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=3, ncols=2) ax[0][0].text(0.1, 0.5, "11") ax[0][1].text(0.1, 0.5, "12") ax[1][0].text(0.1, 0.5, "21") ax[1][1].text(0.1, 0.5, "22") ax[2][0].text(0.1, 0.5, "31") ax[2][1].text(0.1, 0.5, "32") ax[0][0].set(ylabel="y-label", title="axes 1") plt.show() [[Bild:PythonIng_figure34.png|350px]] Beispiel (Achsenbereiche setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-1, 10, 1, 12)) plt.show() [[Bild:PythonIng_figure35.svg|350px]] Beispiel (Gitter einschalten): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-5, 10, 1, 12)) ax[0].minorticks_on() ax[0].grid(which='minor') ax[1].grid(which='major') plt.show() [[Bild:PythonIng_figure36.svg|350px]] Beispiel (ticks setzen) import matplotlib.pyplot as plt import matplotlib.ticker as tick fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") pos = [0, .2, .5] lab = ["a", "b", "c"] ax.xaxis.set_major_locator(tick.FixedLocator(pos)) ax.xaxis.set_major_formatter(tick.FixedFormatter(lab)) plt.show() [[Bild:PythonIng_figure37.svg|350px]] Beispiel (Achsenkreuz beliebig setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") ax.spines['top'].set_color('none') ax.spines['right'].set_color('none') ax.spines['bottom'].set_position(('data', 0.3)) ax.spines['left'].set_position(('data', 0.5)) plt.show() [[Bild:PythonIng_figure38.svg|350px]] Beispiel (Inset): import matplotlib.pyplot as plt # Hauptplot erstellen fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") # Inset hinzu: axin = ax.inset_axes([0.4, 0.4, 0.3, 0.3]) axin.text(0, 0, "Servus") axin.plot() plt.show() [[Bild:PythonIng_figure40.svg|350px]] Beispiel (Hintergrundfarben setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.set_facecolor("green") fig.set_facecolor("yellow") plt.plot([1, 3], [6, 8]) plt.show() [[Bild:PythonIng_figure39.svg|300px]] === GridSpec === Mit figures und axes lassen sich schon jede Menge an Grafikdetails einstellen. GridSpec ist dann der Turbo oder Raketenantrieb den matplotlib eingebaut hat. Naja, vielleicht ist das etwas übertrieben, aber GridSpec ist sozusagen ein flexibler Layout-Manager. Bisher haben wir immer gleich große axes gezeichnet. Mit GridSpec können wir Subplots über mehrere Zeilen und Spalten ziehen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) gs = fig.add_gridspec(3, 3) ax1 = fig.add_subplot(gs[0, :]) ax1.set_title("Bereich 1: gs[0, :]") ax2 = fig.add_subplot(gs[1:, 0]) ax2.set_title("Bereich 2: gs[1:, 0]") ax3 = fig.add_subplot(gs[1, 1:]) ax3.set_title("Bereich 3: gs[1, 1:]") ax4 = fig.add_subplot(gs[2, 1:]) ax4.set_title("Bereich 4: gs[2, 1:]") plt.show() [[Bild:PythonIng_gridspec1.svg|350px]] Erklärung zu <code>(gs[zeile, spalte])</code>: Slicing; damit legen Sie fest, welche Zellen ein Subplot belegt. Achtung! Die Indizes beginnen bei 0 und nicht bei 1 (d.h., die erste Zeile/Spalte hat den Index 0) * : ... alle * 1: ... 1 bis Ende * etc. <code>constrained_layout=True</code> sorgt dafür, dass sich die Subplots nicht überlappen. Aber auch die Spaltenbreiten und Zeilenhöhen lassen sich anpassen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) # Verhältnis erste zu zweite Zeile = 1.5:1 # Verhältnis erste zu zweite Spalte = 1:4 gs = fig.add_gridspec(2, 2, figure=fig, height_ratios=[1.5, 1], width_ratios=[1, 4]) ax1 = fig.add_subplot(gs[0, 0]) ax1.set_title("Bereich 1: gs[0, 0]") ax2 = fig.add_subplot(gs[0, 1]) ax2.set_title("Bereich 2: gs[0, 0]") ax3 = fig.add_subplot(gs[1, 0]) ax3.set_title("Bereich 3: gs[1, 0]") ax4 = fig.add_subplot(gs[1, 1:]) ax4.set_title("Bereich 4: gs[1, 1]") plt.show() [[Bild:PythonIng_gridspec2.svg|350px]] Auch die Ränder lassen sich einstellen. Beispiel 1: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.2, hspace=0.2) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec3.svg|300px]] * hspace ... height space, vertikaler Abstand zwischen den Zeilen * wspace ... width space, horizontaler Abstand zwischen den Spalten Und im Vergleich dazu Beispiel 2: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.3, right=0.7, bottom=0.3, top=0.7, wspace=0.3, hspace=0.3) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec4.svg|300px]] Es gibt noch einige weitere Details, die sich mit GridSpec variieren lassen (z.B. SubGridSpec ... verschachtelte Grid-Layouts). Aber wir belassen es vorerst dabei. Das bisher Gezeigte sollte für die meisten Probleme ausreichend sein. === 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|300px|center]] * 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|300px|center]] * 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. * Zeichnen Sie folgendes Bild (die Achsenbeschriftungen positionieren Sie z.B. mit den Befehlen <code>ax.set_xlabel("x", loc="right"); ax.set_ylabel("y", loc="top")</code>): [[Bild:PythonIng_aufgabe24.svg|400px|center]] * Zeichnen Sie mit Python, matplotlib und NumPy folgendes Bild: [[Bild:PythonIng_figure41.svg|400px|center]] * Zeichnen Sie mit GridSpec ein Layout, das in etwa wie folgendes aussieht (<code>height_ratios=[1, 2], width_ratios=[2, 1]</code>): [[Bild:PythonIng_aufgabe23.svg|300px|center]] == 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 und fortgeschrittenes 3D == === 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 build123d === Hiermit lassen sich einfach 3D-Modelle für CAD-Systeme erstellen. [https://build123d.readthedocs.io/en/latest/ build123d] ist ein externes Modul und muss vorab, wie schon für mehrere andere Module gezeigt, installiert werden (pip, venv etc.). Beispielcode: from build123d import * # Bauteil with BuildPart() as bauteil: # Zylinder erstellen Cylinder(100, 60) # Bohrung with BuildSketch(bauteil.faces().sort_by(Axis.Z)[-1]) as loch: Circle(radius=10) # negativ extrudieren extrude(amount=-60, mode=Mode.SUBTRACT) # Dem Bauteil z.B. die Farbe rot zuweisen # bauteil.part.color = Color("red", alpha=1.0) # Fertiges Modell exportieren export_step(bauteil.part, "bauteil.step") Ausgabe ({{W|Standard for the exchange of product model data | STEP}}-Datei, z.B. in {{W|FreeCAD}} importiert, Screenshot): [[Datei:PythonIng_build123d.jpg | 300px]] FreeCAD ist ein freies 3D-CAD-System. Die [[Ing_Mathematik:_Python#FreeCAD | Makroprogrammierung]] in diesem System wird später noch gezeigt. STEP steht für "'''St'''andard for the '''e'''xchange of '''p'''roduct model data" und ist ein Datenaustauschformat. === 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> == Sonstige Grafikbibliotheken == === Py5 === Im Buch ''Iacono et al.: Programmieren trainieren, mit über 150 Workouts in Java und Python; 3. Aufl., Hanser, 2023'' werden zahlreiche Aufgaben gestellt, die u.a. mit Processing zu lösen sind. Processing ist eine einfache Programmiersprache und Entwicklungsumgebung. Warum ich das erwähne? Weil in älteren Processing-Versionen auch die Programmiersprache Python 2 abgedeckt war. Bei neueren Versionen gibt es keine Python-Anbindung mehr (getestet nur mit openSUSE Tumbleweed und Python 3). Somit funktioniert der Python-Teil im genannten Buch zum Teil nicht mehr. Ist das tragisch, oder kann das Buch weggeschmissen werden? Nein! Es gibt Alternativen zu Processing. Zum einen die im Buch selbst genannten Bibliotheken tkinter oder pygame. Pygame wird dort auch kurz anhand eines Beispiels thematisiert. Aber noch besser ist es, die py5-Bibliothek zu verwenden (lt. Google KI vom 30. Sept. 2026: ''Der echte, moderne „spirituelle Nachfolger“''). Damit ist die Funktionalität von Processing gewährleistet. Man kann aber beliebige Editoren oder IDEs zur Programmerstellung verwenden, ohne an Processing als IDE gebunden zu sein. Nachfolgend werden ein paar Beispiele zu dieser externen Bibliothek gegeben. Der erste Schritt ist wieder, die py5-Bibliothek zu installieren (z.B. mittels pip in eine virtuelle Umgebung; Python>=3.10 und ein JDK>=17 müssen auch installiert sein (Stand Okt. 2026)). Dann kann es schon mit dem Programmieren losgehen. Beispiel (angelehnt an die ''Aufgabe W.2.3 Haus'' des genannten Buchs, jedoch etwas erweitert): import py5 def setup(): py5.size(600, 600) # Fenstergröße 600px x 600px py5.background(255, 255, 255) # Hintergrundfarbe ... weiss def draw(): # Haus zeichnen py5.fill(255, 255, 255) # Hintergrundfarbe ... weiss py5.stroke(255, 0, 0) # Linienfarbe ... rot py5.stroke_weight(3) # Linienbreite ... 3px py5.rect(150, 200, 300, 300) # Rechteck zeichnen (x, y, b, h) py5.triangle(150, 200, 300, 100, 450, 200) # Dreieck zeichnen py5.circle(250, 300, 70) # Kreis zeichnen (x, y, d) # Bemassung zeichnen py5.stroke(0, 255, 0) py5.stroke_weight(1) py5.line(300, 100, 550, 100) # Linie zeichnen py5.line(450, 200, 550, 200) py5.line(450, 500, 550, 500) py5.line(150, 500, 150, 550) py5.line(450, 500, 450, 550) py5.text_size(20) # Textgroesse py5.fill(0, 255, 0) # Textfarbe py5.text("300", 280, 550) # Text zeichnen py5.text("100", 500, 150) py5.text("300", 500, 350) # Gedrehtes Rechteck zeichnen py5.stroke(255, 255, 0) py5.stroke_weight(3) py5.fill(200, 200, 200) py5.translate(320, 260) py5.rotate(0.3) # Drehung (in Radiant) py5.rect(0, 0, 100, 50) # Startet den Sketch py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_1.png|300px]] Das skizzierte Haus ist mit einer rudimentären Bemaßung versehen und hat zwei Fenster, ein rundes und ein gedrehtes, trübes rechteckiges. Beispiel (Mausoperationen): import py5 def setup(): py5.size(600, 600) py5.background(255, 255, 255) def draw(): pass def mouse_pressed(): py5.text_size(15) py5.fill(0, 0, 255) x = py5.mouse_x y = py5.mouse_y py5.text("(%d, %d)" %(x, y), x, y) # Positionsdaten bei Maus-Klick schreiben py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_2.png|300px]] Obiges Bild zeigt das Resultat nach einigen Mausklicks. Man erkennt, dass links-oben der Koordinatennullpunkt ist. Die x-Achse zeigt nach rechts, die y-Achse nach unten. Beispiel (Fünfeck): import py5 import random def setup(): py5.size(400, 400) py5.no_loop() # statisches Bild (schont die Ressourcen) def draw(): py5.translate(py5.width / 2, py5.height / 2) # KS setzen radius = 150 eck_anzahl = 5 py5.fill(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # Füllfarbe auf Zufallswert setzen py5.stroke(155, 0, 0) py5.stroke_weight(5) # Fünfeck als Polygon zeichnen py5.begin_shape() for i in range(eck_anzahl): winkel = i * py5.TWO_PI / eck_anzahl - py5.HALF_PI x = py5.cos(winkel) * radius y = py5.sin(winkel) * radius py5.vertex(x, y) py5.end_shape(py5.CLOSE) py5.run_sketch() Ausgabe, bspw.: [[Datei:PythonIng_py5_5.png|200px]] <u>Kleine Aufgaben:</u> * Um die Funktionsweise von <code>py5.no_loop()</code> zu verstehen, kommentieren Sie die Anweisung im Beispiel einfach aus, und starten Sie das Programm erneut. * Zeichnen Sie ein Siebeneck. * Was passiert, wenn man <code>py5.CLOSE</code> im <code>py5.end_shape()</code>-Befehl weglässt? Beispiel (Grafik laden und zeichnen): import py5 image1 = None def setup(): global image1 py5.size(600, 600) py5.no_loop() image1 = py5.load_image("PythonIng_py5_1.png") def draw(): py5.image(image1, 0, 0) py5.run_sketch() Hier wird einfach das Bild vom Haus-Beispiel (Screenshot von weiter oben) geladen und gezeichnet. Beispiel (angelehnt an ''W.5.10 Wurfparabel'' des genannten Buches; mit OOP erstellt): import py5 class Wurfparabel(py5.Sketch): def __init__(self, v0, angle): super().__init__() self.v0 = v0 self.angle = self.radians(angle) self.g = 9.81 self.t = 0.0 self.scale = 1.5 def settings(self): self.size(800, 600) def setup(self): self.background(0, 0, 0) def draw(self): self.t += 0.1 self.translate(50, 500) # KS Zeichnen self.fill(255, 255, 0) self.stroke(255, 255, 0) self.stroke_weight(1) self.line(0, 0, 0, -300) self.line(0, 0, 700, 0) # Punkt zeichnen cx = self.v0 * self.cos(self.angle) * self.t * self.scale cy = -(self.v0 * self.sin(self.angle) * self.t - \ self.g/2 * self.t**2) * self.scale self.fill(255, 0, 0) self.stroke(255, 0, 0) self.circle(cx, cy, 3) # Stoppen bei Bodenkontakt if cy >= -5: self.no_loop() def wurfparabel(v0=50, angle=45): sketch = Wurfparabel(v0, angle) sketch.run_sketch() v0 = float(input("v0 = ")) winkel = float(input("winkel = ")) wurfparabel(v0=v0, angle=winkel) Eingabe auf der Konsole: v0 = 60 winkel = 45 Ausgabe: [[Datei:PythonIng_py5_13.png|200px]] Die benötigten Formeln der Bewegungsgleichung kann man leicht herleiten, bzw. man benutzt folgende Grafik als Hilfestellung: [[Datei:Wurfparabel_Zusammenfassung_aktualisierung.png|700px]] <u>Aufgabe:</u> Berechnen Sie den Scheitelpunkt und die Wurfweite bei gegebener Anfangsgeschwindigkeit und gegebenem Abwurfwinkel. Die Abwurfhöhe <math>y_0</math> sei 0. Der Wurf soll auf der Erde stattfinden (g = 9.81 m/s²). Die dazu benötigten Formeln können aus obiger Grafik entnommen werden (oder Sie leiten sie selbst her). Geben Sie die Ergebnisse auf der Konsole aus. Beispiel (eine einfache Animation): import py5 i = 0 def setup(): py5.size(600, 600) py5.frame_rate(5) py5.stroke_weight(20) def draw(): global i py5.background(255, 255, 255) py5.translate(py5.width/2, py5.height/2) py5.rotate(py5.PI/8 * i) py5.line(-100, 0, 100, 0) i += 1 py5.run_sketch() Es wird eine rotierende Linie gezeichnet (z.B. wie bei einem Installationsprozess verwendet - zeigt an, dass der Installationsfortschritt nicht eingefroren ist). Hierbei ist nur aufzupassen, dass der Hintergrund in <code>draw()</code> gesetzt wird, ansonsten würden die Linien additiv gezeichnet. Das ist zwar auch schön, aber eben nicht das gewünschte Resultat. Hiermit belassen wir es mit der py5-Bibliothek und wenden uns wieder "mathematischeren" Themen zu. = 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> standardmäßig aber sehr wohl. Jedoch kann man das bei <code>linspace</code> auch ändern. Dann ist der Stoppwert nicht mehr enthalten: linspace(start, stop, num, endpoint=False) == 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 === Beispiel: 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 Visualisierung: 0 1 2 +-----------+ 0 | 1 | 2 | 3 | +-----------+ 1 | 4 | 5 | '''6''' | +-----------+ Beispiel: import numpy as np A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] B = np.matrix(A) print(B[2::, ::2]) Ausgabe: [['20' '22'] ['30' '32'] ['40' '42']] Mit der NumPy-Bibliothek funktioniert das zweidimensionale Slicing. Mit normalen Listen/Tupels geht das nicht, was wir schon früher thematisiert haben. === 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]. Beispiel: import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: <Compressed Sparse Row sparse array of dtype 'float64' with 5 stored elements and shape (5, 5)> Coords Values (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 csr steht für '''c'''ompressed '''s'''parse '''r'''ow. Andere Möglichkeiten: * coo-Format ('''coo'''rdinate Format): <code>scipy.sparse.coo_array()</code> * lil-Format ('''li'''st of '''l'''ists): <code>scipy.sparse.lil_array()</code> * csc-Format ('''c'''ompressed '''s'''parse '''c'''olumn): <code>scipy.sparse.csc_array()</code> Es gibt auch <code>scipy.sparse.csr_matrix()</code> etc. Diese Befehle sind aber veraltet und es wird empfohlen, statt dessen die <code>..._array()</code>-Funktionen zu verwenden. <u>Aufgaben:</u> Recherchieren Sie im Internet * generell zum Thema "spärlich besetzte Matrizen mit Python". * Was sind die Vor-/Nachteile von csr-arrays gegenüber coo- und lil-arrays? * Wann verwendet man sinnvollerweise csr- und wann csc-arrays? * Wie wandelt man coo-arrays in csr-arrays um? = 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.] Siehe dazu auch das Kapitel [[Ing Mathematik: Numerisches Lösen von linearen Gleichungssystemen]]. Dort wird dieses Themengebiet u.a. mit weitergehenden Python-Skripten vertieft. == 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") <u>Aufgaben</u>: Ermitteln Sie mittels SymPy die <math>\mathcal{L}</math>-Transformierten und dann wieder die <math>\mathcal{L}^{-1}</math>-Transformationen (ähnlich wie beim obigen Beispiel): * <math>\sin(t) \circ\! -\! \bullet \ ?</math> * <math>t^3 \circ\! -\! \bullet \ ?</math> * <math>\text{e}^{\omega t} \circ\! -\! \bullet \ ?</math> =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 [https://mpmath.org/ 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). = pint - mit physikalischen Größen rechnen = [[File:Pint-Glas_UK.jpg|right|100px]] pint ist ein externes Python-Paket zur Manipulation von physikalischen Einheiten. Es muss vor der ersten Benutzung installiert werden (Vorgehensweise wie immer: pip, YaST, via venv etc.). Beispiel: import pint import numpy as np ureg = pint.UnitRegistry() # Einfache Berechnung print(3 * ureg.meter + 4 * ureg.cm) # Mit NumPy npeinzel = [3, 4] * ureg.meter + [2, 5] * ureg.cm print(npeinzel) npsum = np.sum(npeinzel) print(npsum) # Konvertierung von Einheiten npsum_inch = npsum.to(ureg.inch) print(npsum_inch) print(npeinzel.magnitude) print(npeinzel.units) print(npeinzel.dimensionality) Ausgabe: 3.04 meter [3.02 4.05] meter 7.07 meter 278.3464566929134 inch [3.02 4.05] meter [length] Die Dokumentation zu pint findet sich auf [https://pint.readthedocs.io/en/stable/]. =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. <small>Je größer die Anzahl der Datensätze n wird, umso weniger spielt das eine Rolle.</small> == 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. = Pandas kurz erklärt = Hier wird kurz auf das externe Modul Pandas eingegangen. Pandas steht für '''Pan'''el '''Da'''ta und ist ein Modul für die Datenanalyse und -manipulation. == Series == Vorstellbar ist eine "Series" wie eine einzelne Spalte in einem Tabellenkalkulationsprogramm. Beispiel (Index implizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) # Temperaturwerte print(temp) Ausgabe: 0 8.5 1 11.0 2 12.5 3 10.0 4 13.0 5 11.0 6 14.5 dtype: float64 Das Kürzel <code>pd</code> ist Konvention. Es seien Temperaturwerte gegeben. Die erste Spalte bei der Ausgabe ist der Index. Die zweite Spalte enthält die Werte. Beispiel (separater Zugriff auf den Index und auf die Werte): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) print(temp.index) print(temp.values) Ausgabe: RangeIndex(start=0, stop=7, step=1) [ 8.5 11. 12.5 10. 13. 11. 14.5] Beispiel (Index explizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) print("*** Die gesamte Series ***") print(temp) print("*** Temperatur am Donnerstag ***") print(temp["Do"]) print("*** Tage wärmer als 12 Grad ***") temp_gr_12 = temp[temp > 12] print(temp_gr_12) Ausgabe: *** Die gesamte Series *** Mo 8.5 Di 11.0 Mi 12.5 Do 10.0 Fr 13.0 Sa 11.0 So 14.5 dtype: float64 *** Temperatur am Donnerstag *** 10.0 *** Tage wärmer als 12 Grad *** Mi 12.5 Fr 13.0 So 14.5 dtype: float64 == DataFrame == Ein DataFrame entspricht einer Tabelle. Beispiel (Erzeugen eines DataFrames): import pandas as pd tag = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] v1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=tag) v2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=tag) v3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=tag) tab = pd.concat([v1, v2, v3], axis=1) tab.columns = stadt print(tab) print(type(tab)) Ausgabe: Linz Salzburg Innsbruck Mo 1 8 Hallo Di 2 9 16 Mi 3 10 17 Do 4 11 18 Fr 5 12 19 Sa 6 13 20 So 7 14 21 <class 'pandas.core.frame.DataFrame'> Wir haben hier drei Datenspalten (v1, v2, v3) mit den Spaltennamen "Linz", "Salzburg" und "Innsbruck", sowie sieben Zeilen "Mo" bis "So". Beispiel (Zugriff auf eine Spalte): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab["Salzburg"]) Ausgabe: Mo 8 Di 9 Mi 10 Do 11 Fr 12 Sa 13 So 14 Name: Salzburg, dtype: int64 Beispiel (Zugriff auf eine Zeile): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab.loc["Di"]) Ausgabe: Linz 2 Salzburg 9 Innsbruck 16 Name: Di, dtype: object == Grafiken erstellen == Hier sei nur ein Torten- oder Kreisdiagramm gezeichnet: import pandas as pd import matplotlib.pyplot as plt temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) expl = [0, 0.2, 0, 0, 0, 0, 0] temp.plot.pie(figsize=(5,5), explode=expl) plt.show() Ausgabe: [[Datei:IngPython_pandas1.svg|350px]] Die <code>figsize</code> hat nichts mit der Spaltenanzahl zu tun, sondern setzt die Fenster- oder Diagrammgröße in Inches (Breite * Höhe). Mit Pandas kann man auch noch andere Diagrammarten erstellen, z.B. Linien- oder Balkendiagramme. Dies wird hier aber nicht vorgeführt. == Arbeiten mit CSV- und LibreOffice-Dateien == Gegeben sei eine CSV-Datei test1.csv: Mai;Juni;Juli 12;17;45 15;65;65 16;32;23 4;14;7 23;17;Keine Daten verfügbar 67;Keine Daten verfügbar;45 15;24;12 Lesen von dieser CSV-Datei: import pandas as pd tab = pd.read_csv("test1.csv", sep=";") print(tab) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Schreiben in eine CSV-Datei test2.csv: import pandas as pd val = pd.read_csv("test1.csv", sep=";") val.to_csv("test2.csv", index=False) Ausgabedatei: Mai,Juni,Juli 12,17,45 15,65,65 16,32,23 4,14,7 23,17,Keine Daten verfügbar 67,Keine Daten verfügbar,45 15,24,12 Die Ausgabedatei ist beinahe gleich wie die Eingabedatei. Es werden aber Kommas statt Strichpunkten verwendet. Daher kommt auch der Name CSV ('''C'''omma '''S'''eparated '''V'''alue). Will man wieder Strichpunkte, so kann man dies über den Parameter <code>sep=";"</code> in der <code>to_csv</code>-Funktion machen. Um direkt von einer ODS-Datei (LibreOffice Calc, '''O'''pen '''D'''ocument '''S'''preadsheet) zu lesen muss das Modul odfpy extra installiert sein (per YaST, pip etc.). Gelesen wird dann folgendermaßen: import pandas as pd df = pd.read_excel('test1.ods', engine='odf') print(df) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Die Datei "test1.ods" sei die Datei, aus der das obige "test1.csv" generiert wurde. Schreiben kann man ebenfalls in eine ODS-Datei: import pandas as pd daten = { "Name": ["Hugo", "Richie", "Wilhelmine"], "Alter": [38, 30, 29], "Ort": ["Linz", "Innsbruck", "Salzburg"] } tab = pd.DataFrame(daten) with pd.ExcelWriter("test2.ods", engine="odf") as writer: tab.to_excel(writer, sheet_name="Daten", index=False) Es ergibt sich eine "test2.ods"-Datei (Screenshot): [[Datei:IngPython_pandas2.png|250px]] == Sonstiges == Hier seien stichpunktartig einige weitere Features von Pandas angeführt: * {{W|Pivot-Tabelle|Pivot-Tabellen}} (pivot ... Dreh-, Angelpunkt): Zweck - große Datenmengen schnell zusammenfassen, filtern und analysieren. * NaN ('''n'''ot '''a''' '''n'''umber): In einem Datensatz fehlt ein Wert oder ist nicht definiert. * groupby-Funktion: Daten strukturieren, aggregieren oder Zwischensummen bilden. * {{W|Binning#Datenverarbeitung_und_-analyse|Binning}}: Klassenbildung. * Mehrstufige Indizierung (Hierarchical Indexing oder MultiIndex) * Zeitreihen (Time Series) Für tiefergehende und weiterführende Informationen wird auf das Buch von ''Klein: Numerisches Python; Hanser, 2023'' und [https://pandas.pydata.org/ pandas] verwiesen. = 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) Das funktioniert übrigens nicht nur bei Funktionsannotationen, sondern auch mit Variablen etc. from typing import Final # "Konstante", ab Python 3.8 NUMBER: Final = 123 print(NUMBER) NUMBER = 125 print(NUMBER) # Variable a: int = 345 print(a) a = "Irgendwas" print(a) # Variable (Pipe-Symbol), ab Python 3.10 b : int|str = 567 print(b) b = "sonstiges" print(b) Ausgabe mit python: 123 125 345 Irgendwas 567 sonstiges Ausgabe mit mypy: test3.py:6: error: Cannot assign to final name "NUMBER" [misc] test3.py:12: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] Found 2 errors in 1 file (checked 1 source file) Python kennt übrigens keine Konstanten. Man kann diese mittels Großbuchstaben andeuten. Das ist aber nur Konvention, hat aber keine sonstigen Auswirkungen. Oder man verwendet wie oben gezeigt <code>Final</code>. Dann werfen Typchecker Errors aus. ==pydoc== pydoc ist ein Werkzeug, um Python-Module, -Funktionen und -Klassen zu dokumentieren. Beispiel <code>test1.py</code>: """Rechen-Modul. Funktionen die von pydoc gelesen werden. """ def multiply(a, b): """Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a * b def add(a, b): """Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a + b if __name__ == '__main__': print(multiply(3, 5)) print(add(4, 6)) Ausgabe mit <code>python3.13 -m pydoc test1</code> auf der Konsole: Help on module test1: NAME test1 - Rechen-Modul. DESCRIPTION Funktionen die von pydoc gelesen werden. FUNCTIONS add(a, b) Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl multiply(a, b) Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl FILE /home/xyz/tmp/test1.py Siehe zu diesem Thema z.B. auch [https://docs.python.org/3/library/pydoc.html]. Mittels <code>python3.13 -m pydoc -w test1</code> kann man auch eine HTML-Dokumentation erstellen. Wofür <code>if __name__ == '__main__':</code> steht, siehe z.B. etwas später im Abschnitt [[Ing_Mathematik:_Python#Doctests]]. == 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.3 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.5.1 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://build123d.readthedocs.io/en/latest/ build123d] * [https://docs.vtk.org/en/latest/api/python.html VTK] * [https://docs.bokeh.org/en/latest/docs/first_steps.html bokeh] * [https://py5coding.org/ py5] == 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] * [https://pint.readthedocs.io/en/stable/ pint] = 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] * Iacono et al.: Programmieren trainieren; 3. Aufl., Hanser, 2023, ISBN 978-3-446-47766-7 * 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}} j85ytk1j0i6ey17kwm0gz85p323jl32 1092058 1092057 2026-10-10T13:24:52Z Intruder 1513 /* Statische Attribute und Methoden */ 1092058 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 (welche Verzeichnisse im PATH liegen, kann bspw. mit <code>echo $PATH</code> abgefragt werden). 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 == === Tests === 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) === Popularität === * An Beliebtheit rangiert Python mit Stand September 2026 mit einem Rating von 17,76% 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 Q1/2026 auf Rang 2, vor TypeScript und hinter JavaScript. * [https://pypl.github.io/PYPL.html PYPL] (Popularity of Programming Language Index) ist ein anderer Popularitätsindex für Programmiersprachen. Auch hier liegt Python mit großem Vorsprung auf dem ersten Platz (52,08%), gefolgt von Java (13,34%) und C/C++ (7,99%) (Stand September 2026). === Namensgebung === 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 in PyConUS24.jpg|{{W|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 <code>help</code> oder <code>help()</code> 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, bzw. der Eingabe von q, quit oder exit mit anschließendem Drücken der RETURN-Taste. == 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 Theoretisch kann man auch Umlaute, griechische Buchstaben u.ä. in Variablenbezeichnern nutzen, z.B. ößÜ = 16 α = 134 print(ößÜ) print(α) Ausgabe: 16 134 Davon wird aber aus offensichtlichen Gründen ({{W|Englisch als Lingua franca}}) abgeraten. Das Eurozeichen (€) etc. darf man ohnehin nicht in Variablenbezeichnern verwenden, dies ergäbe einen Syntaxfehler. Siehe dazu auch [https://peps.python.org/pep-3131/]. 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. Man beachte, dass {{W|Rundungsfehler}} auftreten können. D.h. man sollte bei der Verwendung von {{W|Gleitkommazahl|Gleitpunktzahlen}} niemals auf absolute Genauigkeit prüfen, sondern eine Toleranz (einen <math>\varepsilon</math>-Wert) vorgeben. Ein klassisches Beispiel, das ein falsches Ergebnis liefert, ist: i = 0.1 + 0.2 print(i) if i == 0.3: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 NOK Und das, obwohl man schon in der Volksschule lernt, dass 0.1 + 0.2 gleich 0.3 ist. Korrekt muss das obige Beispiel z.B. so umgeschrieben werden: ist = 0.1 + 0.2 soll = 0.3 epsilon = 0.000001 print(ist) if abs(ist-soll) < epsilon: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 OK Alternativ kann auch die <code>math.isclose()</code>-Funktion verwendet werden. Das sei aber hier nur erwähnt und nicht konkret ausgeführt. <u>Aufgabe:</u> Recherchieren Sie im Internet zum Thema <code>math.isclose()</code>-Funktion. === 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 == Kopfgesteuerte Schleife, Endlosschleife und fußgesteuerte Schleife: [[File:Programmingloops.svg|450px]] === 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 Der <code>break</code>-Befehl wird etwas später noch einmal kurz thematisiert. === 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!") print("Setze Programm fort!") Ausgabe: Das Ergebnis ist unendlich Setze Programm fort! 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). Danach wird das Programm fortgesetzt. 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. Neu in Python 3.14 ist, dass mehrere Ausnahmen auch ohne Klammern angegeben werden können, z.B.: bis inkl. Python 3.13 nur: except (ZeroDivisionError, ValueError): ab Python 3.14 auch: except ZeroDivisionError, ValueError: == 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 c, d = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(c) print(d) Ausgabe: 8 15 Es werden r1 und r2 zurückgegeben und den Variablen c und d zugewiesen. (r1, r2) und (c, d) sind Tupel. Was Tupel konkret sind, siehe etwas später in diesem Kapitel. 3 und 5 sind die aktuellen Parameter (die Argumente). a und b sind die formalen Parameter. Die Zuweisung der aktuellen zu den formalen Parametern erfolgt in der gegebenen Reihenfolge, also die Zahl 3 wird an a übergeben und die Zahl 5 an b. Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 c, d = mathFunc(3, 5) print(c) print(d) e, f = mathFunc(5) print(e) print(f) g, h = mathFunc(b=6) print(g) print(h) Ausgabe: 8 15 25 100 16 60 Erklärung: * Erster <code>mathFunc</code>-Aufruf: Es werden, wie weiter oben, die Argumente 3 und 5 an die formalen Parameter a und b übergeben. Die Rückgabewerte r1 und r2 werden an c und d übergeben. Somit ist c=8 und d=15. * Zweiter <code>mathFunc</code>-Aufruf: Es wird nur das Argment 5 an a übergeben. b behält den Vorgabewert 20. D.h. e= 25 und f=100. * Dritter <code>mathFunc</code>-Aufruf: Es wird nur die Zahl 6 an b übergeben, a behält seinen Vorgabewert 10. Somit ergibt sich g=16 und h=60. Beispiel (Schlüsselwortargumente vs. Positionsargumente): def mathFunc(a, b): r1 = a - b r2 = a / b return r1, r2 c, d = mathFunc(b=3, a=5) # Schlüsselwortargumente e, f = mathFunc(3, 5) # Positionsargumente print(c) print(d) print(e) print(f) Ausgabe: 2 1.6666666666666667 -2 0.6 Durch die Verwendung von Schlüsselwortargumenten können die Argumente in beliebiger Reihenfolge angegeben werden. Bei den Positionsargumenten ist die Reihenfolge vorgegeben. Daraus resultiert auch der Unterschied bei den obigen Ausgaben. Werden beide Arten in einem Aufruf gemischt, so müssen die Positionsargumente zuerst angegeben werden. Den Unterschied zwischen beiden Argumentarten zu kennen ist wichtig, nicht nur bei selbst erstellten Funktionen. Auch beim Aufruf von Python-Funktionen verfolgt einen die Mischung von Schlüsselwort- und Positionsargumenten auf Schritt und Tritt, z.B. beim etwas später folgenden Abschnitt über "Grafiken zeichnen": plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") Bei dieser <code>plot</code>-Funktion aus dem Modul <code>matplotlib.pyplot</code> sind <code>x</code> und <code>y1</code> Positionsargumente. <code>label, lw</code> und <code>ls</code> sind die Namen der Schlüsselwortargumente. Die Schlüsselwortargumente könnten auch in anderer Reihenfolge angeschrieben werden, nicht jedoch die Positionsargumente! Siehe zu <code>matplotlib.pyplot.plot</code> auch die Seite [https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html]. == 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 und Listen == 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. 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. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. # 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 (eine Liste von Wörtern zu einem einzigen zusammenhängenden Text verbinden): woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt == Dictionaries und Mengen == 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'> Leere Mengen lassen sich mit <code>set()</code> erzeugen: menge = set() # leere Menge print(menge) menge.add("Hallo") # Element hinzufügen print(menge) Ausgabe: set() {'Hallo'} Beispiel (einige Mengenoperationen): mengeA = {"Hallo", "Welt"} mengeB = {1, 2, "Welt", 3} # Vereinigung print(mengeA | mengeB) print(mengeA.union(mengeB)) # Durchschnitt print(mengeA & mengeB) print(mengeA.intersection(mengeB)) # Differenz print(mengeA - mengeB) print(mengeA.difference(mengeB)) # Symmetrische Differenz print(mengeA ^ mengeB) print(mengeA.symmetric_difference(mengeB)) # Teilmenge print(mengeA.issubset(mengeB)) # Mehrere Elemente hinzu mengeA.update(["Super", 67, 125]) print(mengeA) # Element löschen mengeA.discard("Hallo") print(mengeA) Ausgabe: {1, 2, 3, 'Welt', 'Hallo'} {1, 2, 3, 'Welt', 'Hallo'} {'Welt'} {'Welt'} {'Hallo'} {'Hallo'} {1, 2, 3, 'Hallo'} {1, 2, 3, 'Hallo'} False {67, 'Super', 'Welt', 'Hallo', 125} {67, 'Super', 'Welt', 125} Es gibt schon seit "ewigen" Zeiten unveränderbare Mengen, die mittels <code>frozenset</code> eingeleitet werden. Neu ab Python 3.15 ist dagegen das Pendant <code>frozendict</code>, das unveränderbare Dictionaries darstellt. fdict = frozendict({"vorname":"Hugo", "nachname":"Meister"}) fset = frozenset({"A", 1, "A"}) print(fdict) print(fdict["vorname"]) print (fset) try: fdict["vorname"] = "Max" print(fdict["vorname"]) except TypeError as e: print(f"Fehler: {e}") try: fset.add("B") print(fset) except AttributeError as e: print(f"Fehler: {e}") Ausgabe: frozendict({'vorname': 'Hugo', 'nachname': 'Meister'}) Hugo frozenset({1, 'A'}) Fehler: 'frozendict' object does not support item assignment Fehler: 'frozenset' object has no attribute 'add' == 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: x = ("a", "b", "c", "d", "e") print(x[2]) print(x[-3]) print(x) print(x[:]) print(x[2:]) print(x[:2]) print(x[::-1]) Ausgabe: c c ('a', 'b', 'c', 'd', 'e') ('a', 'b', 'c', 'd', 'e') ('c', 'd', 'e') ('a', 'b') ('e', 'd', 'c', 'b', 'a') Visualisierung: '''a''' '''b''' '''c''' '''d''' '''e''' +-----------------------------+ | 0 | 1 |//'''2'''//| 3 | 4 | +-----------------------------+ | -5 | -4 |/'''-3'''//| -2 | -1 | +-----------------------------+ |/////////////////////////////| +-----------------------------+ |/////////////''':'''///////////////| +-----------------------------+ | | |///////'''2:'''////////| +-----------------------------+ |////''':2'''/////| | | | +-----------------------------+ |\\\\\<--\\\\\'''::-1'''\\\\<--\\\\\| +-----------------------------+ Beispiel: x = ("a", "b", "c", "d", "e") print(x[2::2]) print(x[2::-1]) print(x[2:0:-1]) Ausgabe: ('c', 'e') ('c', 'b', 'a') ('c', 'b') Hier sieht man sehr schön, dass beim letzen <code>print</code>-Befehl die 0, die in diesem Fall den Buchstaben "a" repräsentiert, als letzer Wert nicht mehr dazugehört (wie weiter oben angedeutet ist der End- oder Stoppwert exklusiv). Will man den Buchstaben "a" dazunehmen, so ist die vorletzte Zeile die richtige. Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH Mit zweidimensionalen Listen/Tupels wird das Ganze schon bedeutend komplexer. Beispiel: Verkettetes Slicing A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] print(A[2::][::2]) Ausgabe: [['20', '21', '22', '23'], ['40', '41', '42', '43']] Das erste Slicing erstellt folgenden Slice: [["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] Das zweite Slicing wird dann auf diesen Slice angewendet und ergibt das genannte Endergebnis. Will man Spalten statt Zeilen, so funktioniert dieser direkte Weg nicht. Man verwendet dann List Comprehensions oder die NumPy-Bibliothek. Diese Bibliothek erlaubt zweidimensionales Slicing. Sie wird erst später bei den Grafiken eingeführt. Deshalb wird hier dazu noch kein Beispiel gezeigt. = 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 (ist der sogenannte Konstruktor). 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 strikt/stark private ist. Wird nur ein Unterstrich verwendet, so deutet dies auf schwach/weakly private hin. Letzteres wird in Python deutlich häufiger verwendet. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. Besonderheiten bei stark und schwach private: * '''stark private''': ** zwei führende Unterstriche ** Name Mangling: Name wird zu <code>_Klassenname__variablenname</code> ** Schutz vor Namenskollisionen bei Vererbung ** am Ende des Namens dürfen keine zwei Unterstriche verwendet werden. Das wären dann Magic Methods oder Dunder-Methods (z.B. __init__). Diese sind public. * '''schwach private''': ** ein führender Unterstrich ** Name bleibt gleich ** ist nur ein Hinweis an die Programmierer, kein starker Schutz ** <code>from modul import *</code>: Objekte mit einem führenden Unterstrich werden standardmäßig nicht importiert ** Standard für die Kapselung bei Python. Schätzung: Weit mehr als 80% der nicht-öffentlichen Attribute/Methoden werden als schwach private deklariert. Weniger als 5-10% als stark private (Quelle: Google KI) == 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 == === Einfachvererbung === [[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 === Mehrfachvererbung === Mit Python ist auch Mehrfachvererbung möglich, z.B.: class A: def _printA(self): print("Klasse A") class B: def _printB(self): print("Klasse B") class C(A,B): def __init__(self): self._printA() self._printB() c = C() Ausgabe: Klasse A Klasse B Die Klasse C erbt von A und B. In UML sieht das in etwa so aus: +-----------------------------+ +-------------------------+ | A | | B | +-----------------------------+ +-------------------------+ | | | | +-----------------------------+ +-------------------------+ | # printA() | | # printB() | +-----------------------------+ +-------------------------+ . . / \ / \ --- --- | | | | +---------------------+ | C | +---------------------+ | | +---------------------+ | | +---------------------+ <code>printA()</code> und <code>printB()</code> sind schwach private (in UML als <code>protected</code> mit einem # dargestellt). Es ist aber zu beachten, dass schwach private nur Konvention ist. Praktisch könnte man auch von außerhalb auf diese Methoden zugreifen. Das soll man aber nicht tun, und das wird in Python durch einen Unterstrich angedeutet. == Statische Attribute und Methoden == Beispiel: class Math: pi = 3.14159265 cnt = 0 @staticmethod def expo(): return 2.71828183 @classmethod def counterPlus(cls): cls.cnt += 1 print(Math.pi) print(Math.expo()) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Ausgabe: 3.14159265 2.71828183 1 2 3 Statische Attribute und Methoden, bzw. Klassenmethoden, sind direkt einer Klasse zugeordnet und nicht einem Objekt. Der Unterschied zwischen statischen Methoden und Klassenmethoden ist, dass statische Methoden nicht auf Klassenattribute (bzw. statische Attribute) zugreifen können, Klassenmethoden aber schon. <code>pi</code> und <code>cnt</code> sind statische Attribute. <code>expo()</code> ist eine statische Methode. <code>counterPlus(cls)</code> ist eine Klassenmethode. Die Bezeichnung des Parameters als <code>cls</code> ist reine Konvention, man sollte sich aber daran halten. = 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. Die "arange"-Funktion aus der NumPy-Bibliothek wird später ein bisschen genauer behandelt (siehe [[Ing_Mathematik:_Python#Zahlenfolgen|Zahlenfolgen]]). <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. Die Farbe des Graphen kann man über den <code>color</code>-Schalter einstellen, z.B. auf rot setzen: plt.plot(x, y, color="red") Das ist nur eine Möglichkeit, es gibt auch andere, z.B. mittels * hexadezimaler Zahl, z.B. <code>color="#ff0000"</code> * Kurzform, z.B. <code>"r"</code> Statt <code>color</code> kann auch die Kurzform <code>c</code> verwendet werden. <u>Kleine Aufgabe:</u> Probieren Sie diese "Farbspielereien" am Computer aus. === Graphen mehrerer Funktionen und weiteres === ==== Mehrere Funktionen in einen Plot zeichnen ==== 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]] Einige weitere Bespiele zu <code>ls</code></code>: * - ... durchgezogene Linie * -. ... Strichpunkt-Linie * dashed ... gestrichelte Linie ==== Marker ==== Desweiteren kann man Marker verwenden. Beispiel: import matplotlib.pyplot as plt plt.plot(10, 10, "ob", markersize=30) plt.plot(20,20, ".r") plt.plot(30,30, "Dg", ms=15) plt.plot(40, 40, "ok", ms=20, mec="#FF5733", mew=3) plt.plot(50, 50, "vk", ms=20, mec="yellow", mew=3) plt.grid() plt.show() Das erste Zeichen im String nach den Koordinaten im plot-Befehl symbolisiert die Form (z.B. o für einen Kreis). Das zweite Zeichen steht für die Farbe (z.B. b für blue). <code>markersize</code> oder <code>ms</code> setzt die Größe des Markers. Ausgabe: [[Bild:PythonIng_formatpara1.svg|200px]] Einige weitere Marker: * * ... Stern * + ... Plus * h oder H ... Sechseck Man kann mit den Parametern * <code>markeredgecolor</code> oder kurz <code>mec</code> die Farbe des Markerrandes * <code>markeredgewidth</code> oder kurz <code>mew</code> die Dicke des Randes * <code>markerfacecolor</code> oder kurz <code>mfc</code> die Innenfarbe des Markers bestimmen. === 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]] === Anmerkungen hinzufügen === Anmerkungen (engl. annotations) lassen sich mit matplotlib einfach hinzufügen. Unter Anmerkung wird hier ein Pfeil mit Text verstanden. Beispiel: import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot([1,10], [1,10]) ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0)) plt.show() Ausgabe: [[Datei: PythonIng_ann1.svg|400px]] <code>shrink</code> verkürzt den Pfeil. Sinnvoll sind beim obigen Beispiel Werte irgendwo zwischen 0.0 und 0.3. <u>Kleine Aufgabe:</u> Probieren Sie verschiedene Pfeillängen aus. Die Schriftgröße verändern können Sie übrigens mit <code>fontsize</code> oder <code>size</code>, z.B. ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0), fontsize=25) Eine andere Schreibweise für Dictionaries, die wir weiter oben schon kennengelernt haben, die wir aber nochmals explizit aufschreiben wollen, ist ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops={"facecolor":"red", "shrink":0.0}) === Figure, Axes etc.=== [[Bild:PythonIng_figure33.svg|350px]] * fig (figure) ... das übergeordnete Fenster * ax (axes) ... "Plot", das eigentliche Zeichenfenster * axis ... die Koordinatenachsen * spine ... Begrenzungslinien des Zeichenfensters * tick ... Markierung an den Achsen des Zeichenfensters Beispiel (eine figure mit 6 axes, ax-Titel, y-axis-Titel): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=3, ncols=2) ax[0][0].text(0.1, 0.5, "11") ax[0][1].text(0.1, 0.5, "12") ax[1][0].text(0.1, 0.5, "21") ax[1][1].text(0.1, 0.5, "22") ax[2][0].text(0.1, 0.5, "31") ax[2][1].text(0.1, 0.5, "32") ax[0][0].set(ylabel="y-label", title="axes 1") plt.show() [[Bild:PythonIng_figure34.png|350px]] Beispiel (Achsenbereiche setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-1, 10, 1, 12)) plt.show() [[Bild:PythonIng_figure35.svg|350px]] Beispiel (Gitter einschalten): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-5, 10, 1, 12)) ax[0].minorticks_on() ax[0].grid(which='minor') ax[1].grid(which='major') plt.show() [[Bild:PythonIng_figure36.svg|350px]] Beispiel (ticks setzen) import matplotlib.pyplot as plt import matplotlib.ticker as tick fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") pos = [0, .2, .5] lab = ["a", "b", "c"] ax.xaxis.set_major_locator(tick.FixedLocator(pos)) ax.xaxis.set_major_formatter(tick.FixedFormatter(lab)) plt.show() [[Bild:PythonIng_figure37.svg|350px]] Beispiel (Achsenkreuz beliebig setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") ax.spines['top'].set_color('none') ax.spines['right'].set_color('none') ax.spines['bottom'].set_position(('data', 0.3)) ax.spines['left'].set_position(('data', 0.5)) plt.show() [[Bild:PythonIng_figure38.svg|350px]] Beispiel (Inset): import matplotlib.pyplot as plt # Hauptplot erstellen fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") # Inset hinzu: axin = ax.inset_axes([0.4, 0.4, 0.3, 0.3]) axin.text(0, 0, "Servus") axin.plot() plt.show() [[Bild:PythonIng_figure40.svg|350px]] Beispiel (Hintergrundfarben setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.set_facecolor("green") fig.set_facecolor("yellow") plt.plot([1, 3], [6, 8]) plt.show() [[Bild:PythonIng_figure39.svg|300px]] === GridSpec === Mit figures und axes lassen sich schon jede Menge an Grafikdetails einstellen. GridSpec ist dann der Turbo oder Raketenantrieb den matplotlib eingebaut hat. Naja, vielleicht ist das etwas übertrieben, aber GridSpec ist sozusagen ein flexibler Layout-Manager. Bisher haben wir immer gleich große axes gezeichnet. Mit GridSpec können wir Subplots über mehrere Zeilen und Spalten ziehen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) gs = fig.add_gridspec(3, 3) ax1 = fig.add_subplot(gs[0, :]) ax1.set_title("Bereich 1: gs[0, :]") ax2 = fig.add_subplot(gs[1:, 0]) ax2.set_title("Bereich 2: gs[1:, 0]") ax3 = fig.add_subplot(gs[1, 1:]) ax3.set_title("Bereich 3: gs[1, 1:]") ax4 = fig.add_subplot(gs[2, 1:]) ax4.set_title("Bereich 4: gs[2, 1:]") plt.show() [[Bild:PythonIng_gridspec1.svg|350px]] Erklärung zu <code>(gs[zeile, spalte])</code>: Slicing; damit legen Sie fest, welche Zellen ein Subplot belegt. Achtung! Die Indizes beginnen bei 0 und nicht bei 1 (d.h., die erste Zeile/Spalte hat den Index 0) * : ... alle * 1: ... 1 bis Ende * etc. <code>constrained_layout=True</code> sorgt dafür, dass sich die Subplots nicht überlappen. Aber auch die Spaltenbreiten und Zeilenhöhen lassen sich anpassen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) # Verhältnis erste zu zweite Zeile = 1.5:1 # Verhältnis erste zu zweite Spalte = 1:4 gs = fig.add_gridspec(2, 2, figure=fig, height_ratios=[1.5, 1], width_ratios=[1, 4]) ax1 = fig.add_subplot(gs[0, 0]) ax1.set_title("Bereich 1: gs[0, 0]") ax2 = fig.add_subplot(gs[0, 1]) ax2.set_title("Bereich 2: gs[0, 0]") ax3 = fig.add_subplot(gs[1, 0]) ax3.set_title("Bereich 3: gs[1, 0]") ax4 = fig.add_subplot(gs[1, 1:]) ax4.set_title("Bereich 4: gs[1, 1]") plt.show() [[Bild:PythonIng_gridspec2.svg|350px]] Auch die Ränder lassen sich einstellen. Beispiel 1: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.2, hspace=0.2) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec3.svg|300px]] * hspace ... height space, vertikaler Abstand zwischen den Zeilen * wspace ... width space, horizontaler Abstand zwischen den Spalten Und im Vergleich dazu Beispiel 2: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.3, right=0.7, bottom=0.3, top=0.7, wspace=0.3, hspace=0.3) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec4.svg|300px]] Es gibt noch einige weitere Details, die sich mit GridSpec variieren lassen (z.B. SubGridSpec ... verschachtelte Grid-Layouts). Aber wir belassen es vorerst dabei. Das bisher Gezeigte sollte für die meisten Probleme ausreichend sein. === 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|300px|center]] * 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|300px|center]] * 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. * Zeichnen Sie folgendes Bild (die Achsenbeschriftungen positionieren Sie z.B. mit den Befehlen <code>ax.set_xlabel("x", loc="right"); ax.set_ylabel("y", loc="top")</code>): [[Bild:PythonIng_aufgabe24.svg|400px|center]] * Zeichnen Sie mit Python, matplotlib und NumPy folgendes Bild: [[Bild:PythonIng_figure41.svg|400px|center]] * Zeichnen Sie mit GridSpec ein Layout, das in etwa wie folgendes aussieht (<code>height_ratios=[1, 2], width_ratios=[2, 1]</code>): [[Bild:PythonIng_aufgabe23.svg|300px|center]] == 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 und fortgeschrittenes 3D == === 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 build123d === Hiermit lassen sich einfach 3D-Modelle für CAD-Systeme erstellen. [https://build123d.readthedocs.io/en/latest/ build123d] ist ein externes Modul und muss vorab, wie schon für mehrere andere Module gezeigt, installiert werden (pip, venv etc.). Beispielcode: from build123d import * # Bauteil with BuildPart() as bauteil: # Zylinder erstellen Cylinder(100, 60) # Bohrung with BuildSketch(bauteil.faces().sort_by(Axis.Z)[-1]) as loch: Circle(radius=10) # negativ extrudieren extrude(amount=-60, mode=Mode.SUBTRACT) # Dem Bauteil z.B. die Farbe rot zuweisen # bauteil.part.color = Color("red", alpha=1.0) # Fertiges Modell exportieren export_step(bauteil.part, "bauteil.step") Ausgabe ({{W|Standard for the exchange of product model data | STEP}}-Datei, z.B. in {{W|FreeCAD}} importiert, Screenshot): [[Datei:PythonIng_build123d.jpg | 300px]] FreeCAD ist ein freies 3D-CAD-System. Die [[Ing_Mathematik:_Python#FreeCAD | Makroprogrammierung]] in diesem System wird später noch gezeigt. STEP steht für "'''St'''andard for the '''e'''xchange of '''p'''roduct model data" und ist ein Datenaustauschformat. === 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> == Sonstige Grafikbibliotheken == === Py5 === Im Buch ''Iacono et al.: Programmieren trainieren, mit über 150 Workouts in Java und Python; 3. Aufl., Hanser, 2023'' werden zahlreiche Aufgaben gestellt, die u.a. mit Processing zu lösen sind. Processing ist eine einfache Programmiersprache und Entwicklungsumgebung. Warum ich das erwähne? Weil in älteren Processing-Versionen auch die Programmiersprache Python 2 abgedeckt war. Bei neueren Versionen gibt es keine Python-Anbindung mehr (getestet nur mit openSUSE Tumbleweed und Python 3). Somit funktioniert der Python-Teil im genannten Buch zum Teil nicht mehr. Ist das tragisch, oder kann das Buch weggeschmissen werden? Nein! Es gibt Alternativen zu Processing. Zum einen die im Buch selbst genannten Bibliotheken tkinter oder pygame. Pygame wird dort auch kurz anhand eines Beispiels thematisiert. Aber noch besser ist es, die py5-Bibliothek zu verwenden (lt. Google KI vom 30. Sept. 2026: ''Der echte, moderne „spirituelle Nachfolger“''). Damit ist die Funktionalität von Processing gewährleistet. Man kann aber beliebige Editoren oder IDEs zur Programmerstellung verwenden, ohne an Processing als IDE gebunden zu sein. Nachfolgend werden ein paar Beispiele zu dieser externen Bibliothek gegeben. Der erste Schritt ist wieder, die py5-Bibliothek zu installieren (z.B. mittels pip in eine virtuelle Umgebung; Python>=3.10 und ein JDK>=17 müssen auch installiert sein (Stand Okt. 2026)). Dann kann es schon mit dem Programmieren losgehen. Beispiel (angelehnt an die ''Aufgabe W.2.3 Haus'' des genannten Buchs, jedoch etwas erweitert): import py5 def setup(): py5.size(600, 600) # Fenstergröße 600px x 600px py5.background(255, 255, 255) # Hintergrundfarbe ... weiss def draw(): # Haus zeichnen py5.fill(255, 255, 255) # Hintergrundfarbe ... weiss py5.stroke(255, 0, 0) # Linienfarbe ... rot py5.stroke_weight(3) # Linienbreite ... 3px py5.rect(150, 200, 300, 300) # Rechteck zeichnen (x, y, b, h) py5.triangle(150, 200, 300, 100, 450, 200) # Dreieck zeichnen py5.circle(250, 300, 70) # Kreis zeichnen (x, y, d) # Bemassung zeichnen py5.stroke(0, 255, 0) py5.stroke_weight(1) py5.line(300, 100, 550, 100) # Linie zeichnen py5.line(450, 200, 550, 200) py5.line(450, 500, 550, 500) py5.line(150, 500, 150, 550) py5.line(450, 500, 450, 550) py5.text_size(20) # Textgroesse py5.fill(0, 255, 0) # Textfarbe py5.text("300", 280, 550) # Text zeichnen py5.text("100", 500, 150) py5.text("300", 500, 350) # Gedrehtes Rechteck zeichnen py5.stroke(255, 255, 0) py5.stroke_weight(3) py5.fill(200, 200, 200) py5.translate(320, 260) py5.rotate(0.3) # Drehung (in Radiant) py5.rect(0, 0, 100, 50) # Startet den Sketch py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_1.png|300px]] Das skizzierte Haus ist mit einer rudimentären Bemaßung versehen und hat zwei Fenster, ein rundes und ein gedrehtes, trübes rechteckiges. Beispiel (Mausoperationen): import py5 def setup(): py5.size(600, 600) py5.background(255, 255, 255) def draw(): pass def mouse_pressed(): py5.text_size(15) py5.fill(0, 0, 255) x = py5.mouse_x y = py5.mouse_y py5.text("(%d, %d)" %(x, y), x, y) # Positionsdaten bei Maus-Klick schreiben py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_2.png|300px]] Obiges Bild zeigt das Resultat nach einigen Mausklicks. Man erkennt, dass links-oben der Koordinatennullpunkt ist. Die x-Achse zeigt nach rechts, die y-Achse nach unten. Beispiel (Fünfeck): import py5 import random def setup(): py5.size(400, 400) py5.no_loop() # statisches Bild (schont die Ressourcen) def draw(): py5.translate(py5.width / 2, py5.height / 2) # KS setzen radius = 150 eck_anzahl = 5 py5.fill(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # Füllfarbe auf Zufallswert setzen py5.stroke(155, 0, 0) py5.stroke_weight(5) # Fünfeck als Polygon zeichnen py5.begin_shape() for i in range(eck_anzahl): winkel = i * py5.TWO_PI / eck_anzahl - py5.HALF_PI x = py5.cos(winkel) * radius y = py5.sin(winkel) * radius py5.vertex(x, y) py5.end_shape(py5.CLOSE) py5.run_sketch() Ausgabe, bspw.: [[Datei:PythonIng_py5_5.png|200px]] <u>Kleine Aufgaben:</u> * Um die Funktionsweise von <code>py5.no_loop()</code> zu verstehen, kommentieren Sie die Anweisung im Beispiel einfach aus, und starten Sie das Programm erneut. * Zeichnen Sie ein Siebeneck. * Was passiert, wenn man <code>py5.CLOSE</code> im <code>py5.end_shape()</code>-Befehl weglässt? Beispiel (Grafik laden und zeichnen): import py5 image1 = None def setup(): global image1 py5.size(600, 600) py5.no_loop() image1 = py5.load_image("PythonIng_py5_1.png") def draw(): py5.image(image1, 0, 0) py5.run_sketch() Hier wird einfach das Bild vom Haus-Beispiel (Screenshot von weiter oben) geladen und gezeichnet. Beispiel (angelehnt an ''W.5.10 Wurfparabel'' des genannten Buches; mit OOP erstellt): import py5 class Wurfparabel(py5.Sketch): def __init__(self, v0, angle): super().__init__() self.v0 = v0 self.angle = self.radians(angle) self.g = 9.81 self.t = 0.0 self.scale = 1.5 def settings(self): self.size(800, 600) def setup(self): self.background(0, 0, 0) def draw(self): self.t += 0.1 self.translate(50, 500) # KS Zeichnen self.fill(255, 255, 0) self.stroke(255, 255, 0) self.stroke_weight(1) self.line(0, 0, 0, -300) self.line(0, 0, 700, 0) # Punkt zeichnen cx = self.v0 * self.cos(self.angle) * self.t * self.scale cy = -(self.v0 * self.sin(self.angle) * self.t - \ self.g/2 * self.t**2) * self.scale self.fill(255, 0, 0) self.stroke(255, 0, 0) self.circle(cx, cy, 3) # Stoppen bei Bodenkontakt if cy >= -5: self.no_loop() def wurfparabel(v0=50, angle=45): sketch = Wurfparabel(v0, angle) sketch.run_sketch() v0 = float(input("v0 = ")) winkel = float(input("winkel = ")) wurfparabel(v0=v0, angle=winkel) Eingabe auf der Konsole: v0 = 60 winkel = 45 Ausgabe: [[Datei:PythonIng_py5_13.png|200px]] Die benötigten Formeln der Bewegungsgleichung kann man leicht herleiten, bzw. man benutzt folgende Grafik als Hilfestellung: [[Datei:Wurfparabel_Zusammenfassung_aktualisierung.png|700px]] <u>Aufgabe:</u> Berechnen Sie den Scheitelpunkt und die Wurfweite bei gegebener Anfangsgeschwindigkeit und gegebenem Abwurfwinkel. Die Abwurfhöhe <math>y_0</math> sei 0. Der Wurf soll auf der Erde stattfinden (g = 9.81 m/s²). Die dazu benötigten Formeln können aus obiger Grafik entnommen werden (oder Sie leiten sie selbst her). Geben Sie die Ergebnisse auf der Konsole aus. Beispiel (eine einfache Animation): import py5 i = 0 def setup(): py5.size(600, 600) py5.frame_rate(5) py5.stroke_weight(20) def draw(): global i py5.background(255, 255, 255) py5.translate(py5.width/2, py5.height/2) py5.rotate(py5.PI/8 * i) py5.line(-100, 0, 100, 0) i += 1 py5.run_sketch() Es wird eine rotierende Linie gezeichnet (z.B. wie bei einem Installationsprozess verwendet - zeigt an, dass der Installationsfortschritt nicht eingefroren ist). Hierbei ist nur aufzupassen, dass der Hintergrund in <code>draw()</code> gesetzt wird, ansonsten würden die Linien additiv gezeichnet. Das ist zwar auch schön, aber eben nicht das gewünschte Resultat. Hiermit belassen wir es mit der py5-Bibliothek und wenden uns wieder "mathematischeren" Themen zu. = 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> standardmäßig aber sehr wohl. Jedoch kann man das bei <code>linspace</code> auch ändern. Dann ist der Stoppwert nicht mehr enthalten: linspace(start, stop, num, endpoint=False) == 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 === Beispiel: 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 Visualisierung: 0 1 2 +-----------+ 0 | 1 | 2 | 3 | +-----------+ 1 | 4 | 5 | '''6''' | +-----------+ Beispiel: import numpy as np A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] B = np.matrix(A) print(B[2::, ::2]) Ausgabe: [['20' '22'] ['30' '32'] ['40' '42']] Mit der NumPy-Bibliothek funktioniert das zweidimensionale Slicing. Mit normalen Listen/Tupels geht das nicht, was wir schon früher thematisiert haben. === 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]. Beispiel: import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: <Compressed Sparse Row sparse array of dtype 'float64' with 5 stored elements and shape (5, 5)> Coords Values (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 csr steht für '''c'''ompressed '''s'''parse '''r'''ow. Andere Möglichkeiten: * coo-Format ('''coo'''rdinate Format): <code>scipy.sparse.coo_array()</code> * lil-Format ('''li'''st of '''l'''ists): <code>scipy.sparse.lil_array()</code> * csc-Format ('''c'''ompressed '''s'''parse '''c'''olumn): <code>scipy.sparse.csc_array()</code> Es gibt auch <code>scipy.sparse.csr_matrix()</code> etc. Diese Befehle sind aber veraltet und es wird empfohlen, statt dessen die <code>..._array()</code>-Funktionen zu verwenden. <u>Aufgaben:</u> Recherchieren Sie im Internet * generell zum Thema "spärlich besetzte Matrizen mit Python". * Was sind die Vor-/Nachteile von csr-arrays gegenüber coo- und lil-arrays? * Wann verwendet man sinnvollerweise csr- und wann csc-arrays? * Wie wandelt man coo-arrays in csr-arrays um? = 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.] Siehe dazu auch das Kapitel [[Ing Mathematik: Numerisches Lösen von linearen Gleichungssystemen]]. Dort wird dieses Themengebiet u.a. mit weitergehenden Python-Skripten vertieft. == 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") <u>Aufgaben</u>: Ermitteln Sie mittels SymPy die <math>\mathcal{L}</math>-Transformierten und dann wieder die <math>\mathcal{L}^{-1}</math>-Transformationen (ähnlich wie beim obigen Beispiel): * <math>\sin(t) \circ\! -\! \bullet \ ?</math> * <math>t^3 \circ\! -\! \bullet \ ?</math> * <math>\text{e}^{\omega t} \circ\! -\! \bullet \ ?</math> =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 [https://mpmath.org/ 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). = pint - mit physikalischen Größen rechnen = [[File:Pint-Glas_UK.jpg|right|100px]] pint ist ein externes Python-Paket zur Manipulation von physikalischen Einheiten. Es muss vor der ersten Benutzung installiert werden (Vorgehensweise wie immer: pip, YaST, via venv etc.). Beispiel: import pint import numpy as np ureg = pint.UnitRegistry() # Einfache Berechnung print(3 * ureg.meter + 4 * ureg.cm) # Mit NumPy npeinzel = [3, 4] * ureg.meter + [2, 5] * ureg.cm print(npeinzel) npsum = np.sum(npeinzel) print(npsum) # Konvertierung von Einheiten npsum_inch = npsum.to(ureg.inch) print(npsum_inch) print(npeinzel.magnitude) print(npeinzel.units) print(npeinzel.dimensionality) Ausgabe: 3.04 meter [3.02 4.05] meter 7.07 meter 278.3464566929134 inch [3.02 4.05] meter [length] Die Dokumentation zu pint findet sich auf [https://pint.readthedocs.io/en/stable/]. =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. <small>Je größer die Anzahl der Datensätze n wird, umso weniger spielt das eine Rolle.</small> == 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. = Pandas kurz erklärt = Hier wird kurz auf das externe Modul Pandas eingegangen. Pandas steht für '''Pan'''el '''Da'''ta und ist ein Modul für die Datenanalyse und -manipulation. == Series == Vorstellbar ist eine "Series" wie eine einzelne Spalte in einem Tabellenkalkulationsprogramm. Beispiel (Index implizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) # Temperaturwerte print(temp) Ausgabe: 0 8.5 1 11.0 2 12.5 3 10.0 4 13.0 5 11.0 6 14.5 dtype: float64 Das Kürzel <code>pd</code> ist Konvention. Es seien Temperaturwerte gegeben. Die erste Spalte bei der Ausgabe ist der Index. Die zweite Spalte enthält die Werte. Beispiel (separater Zugriff auf den Index und auf die Werte): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) print(temp.index) print(temp.values) Ausgabe: RangeIndex(start=0, stop=7, step=1) [ 8.5 11. 12.5 10. 13. 11. 14.5] Beispiel (Index explizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) print("*** Die gesamte Series ***") print(temp) print("*** Temperatur am Donnerstag ***") print(temp["Do"]) print("*** Tage wärmer als 12 Grad ***") temp_gr_12 = temp[temp > 12] print(temp_gr_12) Ausgabe: *** Die gesamte Series *** Mo 8.5 Di 11.0 Mi 12.5 Do 10.0 Fr 13.0 Sa 11.0 So 14.5 dtype: float64 *** Temperatur am Donnerstag *** 10.0 *** Tage wärmer als 12 Grad *** Mi 12.5 Fr 13.0 So 14.5 dtype: float64 == DataFrame == Ein DataFrame entspricht einer Tabelle. Beispiel (Erzeugen eines DataFrames): import pandas as pd tag = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] v1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=tag) v2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=tag) v3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=tag) tab = pd.concat([v1, v2, v3], axis=1) tab.columns = stadt print(tab) print(type(tab)) Ausgabe: Linz Salzburg Innsbruck Mo 1 8 Hallo Di 2 9 16 Mi 3 10 17 Do 4 11 18 Fr 5 12 19 Sa 6 13 20 So 7 14 21 <class 'pandas.core.frame.DataFrame'> Wir haben hier drei Datenspalten (v1, v2, v3) mit den Spaltennamen "Linz", "Salzburg" und "Innsbruck", sowie sieben Zeilen "Mo" bis "So". Beispiel (Zugriff auf eine Spalte): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab["Salzburg"]) Ausgabe: Mo 8 Di 9 Mi 10 Do 11 Fr 12 Sa 13 So 14 Name: Salzburg, dtype: int64 Beispiel (Zugriff auf eine Zeile): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab.loc["Di"]) Ausgabe: Linz 2 Salzburg 9 Innsbruck 16 Name: Di, dtype: object == Grafiken erstellen == Hier sei nur ein Torten- oder Kreisdiagramm gezeichnet: import pandas as pd import matplotlib.pyplot as plt temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) expl = [0, 0.2, 0, 0, 0, 0, 0] temp.plot.pie(figsize=(5,5), explode=expl) plt.show() Ausgabe: [[Datei:IngPython_pandas1.svg|350px]] Die <code>figsize</code> hat nichts mit der Spaltenanzahl zu tun, sondern setzt die Fenster- oder Diagrammgröße in Inches (Breite * Höhe). Mit Pandas kann man auch noch andere Diagrammarten erstellen, z.B. Linien- oder Balkendiagramme. Dies wird hier aber nicht vorgeführt. == Arbeiten mit CSV- und LibreOffice-Dateien == Gegeben sei eine CSV-Datei test1.csv: Mai;Juni;Juli 12;17;45 15;65;65 16;32;23 4;14;7 23;17;Keine Daten verfügbar 67;Keine Daten verfügbar;45 15;24;12 Lesen von dieser CSV-Datei: import pandas as pd tab = pd.read_csv("test1.csv", sep=";") print(tab) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Schreiben in eine CSV-Datei test2.csv: import pandas as pd val = pd.read_csv("test1.csv", sep=";") val.to_csv("test2.csv", index=False) Ausgabedatei: Mai,Juni,Juli 12,17,45 15,65,65 16,32,23 4,14,7 23,17,Keine Daten verfügbar 67,Keine Daten verfügbar,45 15,24,12 Die Ausgabedatei ist beinahe gleich wie die Eingabedatei. Es werden aber Kommas statt Strichpunkten verwendet. Daher kommt auch der Name CSV ('''C'''omma '''S'''eparated '''V'''alue). Will man wieder Strichpunkte, so kann man dies über den Parameter <code>sep=";"</code> in der <code>to_csv</code>-Funktion machen. Um direkt von einer ODS-Datei (LibreOffice Calc, '''O'''pen '''D'''ocument '''S'''preadsheet) zu lesen muss das Modul odfpy extra installiert sein (per YaST, pip etc.). Gelesen wird dann folgendermaßen: import pandas as pd df = pd.read_excel('test1.ods', engine='odf') print(df) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Die Datei "test1.ods" sei die Datei, aus der das obige "test1.csv" generiert wurde. Schreiben kann man ebenfalls in eine ODS-Datei: import pandas as pd daten = { "Name": ["Hugo", "Richie", "Wilhelmine"], "Alter": [38, 30, 29], "Ort": ["Linz", "Innsbruck", "Salzburg"] } tab = pd.DataFrame(daten) with pd.ExcelWriter("test2.ods", engine="odf") as writer: tab.to_excel(writer, sheet_name="Daten", index=False) Es ergibt sich eine "test2.ods"-Datei (Screenshot): [[Datei:IngPython_pandas2.png|250px]] == Sonstiges == Hier seien stichpunktartig einige weitere Features von Pandas angeführt: * {{W|Pivot-Tabelle|Pivot-Tabellen}} (pivot ... Dreh-, Angelpunkt): Zweck - große Datenmengen schnell zusammenfassen, filtern und analysieren. * NaN ('''n'''ot '''a''' '''n'''umber): In einem Datensatz fehlt ein Wert oder ist nicht definiert. * groupby-Funktion: Daten strukturieren, aggregieren oder Zwischensummen bilden. * {{W|Binning#Datenverarbeitung_und_-analyse|Binning}}: Klassenbildung. * Mehrstufige Indizierung (Hierarchical Indexing oder MultiIndex) * Zeitreihen (Time Series) Für tiefergehende und weiterführende Informationen wird auf das Buch von ''Klein: Numerisches Python; Hanser, 2023'' und [https://pandas.pydata.org/ pandas] verwiesen. = 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) Das funktioniert übrigens nicht nur bei Funktionsannotationen, sondern auch mit Variablen etc. from typing import Final # "Konstante", ab Python 3.8 NUMBER: Final = 123 print(NUMBER) NUMBER = 125 print(NUMBER) # Variable a: int = 345 print(a) a = "Irgendwas" print(a) # Variable (Pipe-Symbol), ab Python 3.10 b : int|str = 567 print(b) b = "sonstiges" print(b) Ausgabe mit python: 123 125 345 Irgendwas 567 sonstiges Ausgabe mit mypy: test3.py:6: error: Cannot assign to final name "NUMBER" [misc] test3.py:12: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] Found 2 errors in 1 file (checked 1 source file) Python kennt übrigens keine Konstanten. Man kann diese mittels Großbuchstaben andeuten. Das ist aber nur Konvention, hat aber keine sonstigen Auswirkungen. Oder man verwendet wie oben gezeigt <code>Final</code>. Dann werfen Typchecker Errors aus. ==pydoc== pydoc ist ein Werkzeug, um Python-Module, -Funktionen und -Klassen zu dokumentieren. Beispiel <code>test1.py</code>: """Rechen-Modul. Funktionen die von pydoc gelesen werden. """ def multiply(a, b): """Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a * b def add(a, b): """Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a + b if __name__ == '__main__': print(multiply(3, 5)) print(add(4, 6)) Ausgabe mit <code>python3.13 -m pydoc test1</code> auf der Konsole: Help on module test1: NAME test1 - Rechen-Modul. DESCRIPTION Funktionen die von pydoc gelesen werden. FUNCTIONS add(a, b) Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl multiply(a, b) Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl FILE /home/xyz/tmp/test1.py Siehe zu diesem Thema z.B. auch [https://docs.python.org/3/library/pydoc.html]. Mittels <code>python3.13 -m pydoc -w test1</code> kann man auch eine HTML-Dokumentation erstellen. Wofür <code>if __name__ == '__main__':</code> steht, siehe z.B. etwas später im Abschnitt [[Ing_Mathematik:_Python#Doctests]]. == 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.3 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.5.1 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://build123d.readthedocs.io/en/latest/ build123d] * [https://docs.vtk.org/en/latest/api/python.html VTK] * [https://docs.bokeh.org/en/latest/docs/first_steps.html bokeh] * [https://py5coding.org/ py5] == 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] * [https://pint.readthedocs.io/en/stable/ pint] = 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] * Iacono et al.: Programmieren trainieren; 3. Aufl., Hanser, 2023, ISBN 978-3-446-47766-7 * 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}} cwskdwdmbu1rhk35kzwetf9yl7uxtk2 1092059 1092058 2026-10-10T13:28:31Z Intruder 1513 /* Statische Attribute und Methoden */ Überschrift angepasst 1092059 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 (welche Verzeichnisse im PATH liegen, kann bspw. mit <code>echo $PATH</code> abgefragt werden). 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 == === Tests === 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) === Popularität === * An Beliebtheit rangiert Python mit Stand September 2026 mit einem Rating von 17,76% 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 Q1/2026 auf Rang 2, vor TypeScript und hinter JavaScript. * [https://pypl.github.io/PYPL.html PYPL] (Popularity of Programming Language Index) ist ein anderer Popularitätsindex für Programmiersprachen. Auch hier liegt Python mit großem Vorsprung auf dem ersten Platz (52,08%), gefolgt von Java (13,34%) und C/C++ (7,99%) (Stand September 2026). === Namensgebung === 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 in PyConUS24.jpg|{{W|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 <code>help</code> oder <code>help()</code> 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, bzw. der Eingabe von q, quit oder exit mit anschließendem Drücken der RETURN-Taste. == 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 Theoretisch kann man auch Umlaute, griechische Buchstaben u.ä. in Variablenbezeichnern nutzen, z.B. ößÜ = 16 α = 134 print(ößÜ) print(α) Ausgabe: 16 134 Davon wird aber aus offensichtlichen Gründen ({{W|Englisch als Lingua franca}}) abgeraten. Das Eurozeichen (€) etc. darf man ohnehin nicht in Variablenbezeichnern verwenden, dies ergäbe einen Syntaxfehler. Siehe dazu auch [https://peps.python.org/pep-3131/]. 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. Man beachte, dass {{W|Rundungsfehler}} auftreten können. D.h. man sollte bei der Verwendung von {{W|Gleitkommazahl|Gleitpunktzahlen}} niemals auf absolute Genauigkeit prüfen, sondern eine Toleranz (einen <math>\varepsilon</math>-Wert) vorgeben. Ein klassisches Beispiel, das ein falsches Ergebnis liefert, ist: i = 0.1 + 0.2 print(i) if i == 0.3: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 NOK Und das, obwohl man schon in der Volksschule lernt, dass 0.1 + 0.2 gleich 0.3 ist. Korrekt muss das obige Beispiel z.B. so umgeschrieben werden: ist = 0.1 + 0.2 soll = 0.3 epsilon = 0.000001 print(ist) if abs(ist-soll) < epsilon: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 OK Alternativ kann auch die <code>math.isclose()</code>-Funktion verwendet werden. Das sei aber hier nur erwähnt und nicht konkret ausgeführt. <u>Aufgabe:</u> Recherchieren Sie im Internet zum Thema <code>math.isclose()</code>-Funktion. === 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 == Kopfgesteuerte Schleife, Endlosschleife und fußgesteuerte Schleife: [[File:Programmingloops.svg|450px]] === 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 Der <code>break</code>-Befehl wird etwas später noch einmal kurz thematisiert. === 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!") print("Setze Programm fort!") Ausgabe: Das Ergebnis ist unendlich Setze Programm fort! 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). Danach wird das Programm fortgesetzt. 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. Neu in Python 3.14 ist, dass mehrere Ausnahmen auch ohne Klammern angegeben werden können, z.B.: bis inkl. Python 3.13 nur: except (ZeroDivisionError, ValueError): ab Python 3.14 auch: except ZeroDivisionError, ValueError: == 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 c, d = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(c) print(d) Ausgabe: 8 15 Es werden r1 und r2 zurückgegeben und den Variablen c und d zugewiesen. (r1, r2) und (c, d) sind Tupel. Was Tupel konkret sind, siehe etwas später in diesem Kapitel. 3 und 5 sind die aktuellen Parameter (die Argumente). a und b sind die formalen Parameter. Die Zuweisung der aktuellen zu den formalen Parametern erfolgt in der gegebenen Reihenfolge, also die Zahl 3 wird an a übergeben und die Zahl 5 an b. Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 c, d = mathFunc(3, 5) print(c) print(d) e, f = mathFunc(5) print(e) print(f) g, h = mathFunc(b=6) print(g) print(h) Ausgabe: 8 15 25 100 16 60 Erklärung: * Erster <code>mathFunc</code>-Aufruf: Es werden, wie weiter oben, die Argumente 3 und 5 an die formalen Parameter a und b übergeben. Die Rückgabewerte r1 und r2 werden an c und d übergeben. Somit ist c=8 und d=15. * Zweiter <code>mathFunc</code>-Aufruf: Es wird nur das Argment 5 an a übergeben. b behält den Vorgabewert 20. D.h. e= 25 und f=100. * Dritter <code>mathFunc</code>-Aufruf: Es wird nur die Zahl 6 an b übergeben, a behält seinen Vorgabewert 10. Somit ergibt sich g=16 und h=60. Beispiel (Schlüsselwortargumente vs. Positionsargumente): def mathFunc(a, b): r1 = a - b r2 = a / b return r1, r2 c, d = mathFunc(b=3, a=5) # Schlüsselwortargumente e, f = mathFunc(3, 5) # Positionsargumente print(c) print(d) print(e) print(f) Ausgabe: 2 1.6666666666666667 -2 0.6 Durch die Verwendung von Schlüsselwortargumenten können die Argumente in beliebiger Reihenfolge angegeben werden. Bei den Positionsargumenten ist die Reihenfolge vorgegeben. Daraus resultiert auch der Unterschied bei den obigen Ausgaben. Werden beide Arten in einem Aufruf gemischt, so müssen die Positionsargumente zuerst angegeben werden. Den Unterschied zwischen beiden Argumentarten zu kennen ist wichtig, nicht nur bei selbst erstellten Funktionen. Auch beim Aufruf von Python-Funktionen verfolgt einen die Mischung von Schlüsselwort- und Positionsargumenten auf Schritt und Tritt, z.B. beim etwas später folgenden Abschnitt über "Grafiken zeichnen": plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") Bei dieser <code>plot</code>-Funktion aus dem Modul <code>matplotlib.pyplot</code> sind <code>x</code> und <code>y1</code> Positionsargumente. <code>label, lw</code> und <code>ls</code> sind die Namen der Schlüsselwortargumente. Die Schlüsselwortargumente könnten auch in anderer Reihenfolge angeschrieben werden, nicht jedoch die Positionsargumente! Siehe zu <code>matplotlib.pyplot.plot</code> auch die Seite [https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html]. == 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 und Listen == 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. 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. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. # 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 (eine Liste von Wörtern zu einem einzigen zusammenhängenden Text verbinden): woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt == Dictionaries und Mengen == 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'> Leere Mengen lassen sich mit <code>set()</code> erzeugen: menge = set() # leere Menge print(menge) menge.add("Hallo") # Element hinzufügen print(menge) Ausgabe: set() {'Hallo'} Beispiel (einige Mengenoperationen): mengeA = {"Hallo", "Welt"} mengeB = {1, 2, "Welt", 3} # Vereinigung print(mengeA | mengeB) print(mengeA.union(mengeB)) # Durchschnitt print(mengeA & mengeB) print(mengeA.intersection(mengeB)) # Differenz print(mengeA - mengeB) print(mengeA.difference(mengeB)) # Symmetrische Differenz print(mengeA ^ mengeB) print(mengeA.symmetric_difference(mengeB)) # Teilmenge print(mengeA.issubset(mengeB)) # Mehrere Elemente hinzu mengeA.update(["Super", 67, 125]) print(mengeA) # Element löschen mengeA.discard("Hallo") print(mengeA) Ausgabe: {1, 2, 3, 'Welt', 'Hallo'} {1, 2, 3, 'Welt', 'Hallo'} {'Welt'} {'Welt'} {'Hallo'} {'Hallo'} {1, 2, 3, 'Hallo'} {1, 2, 3, 'Hallo'} False {67, 'Super', 'Welt', 'Hallo', 125} {67, 'Super', 'Welt', 125} Es gibt schon seit "ewigen" Zeiten unveränderbare Mengen, die mittels <code>frozenset</code> eingeleitet werden. Neu ab Python 3.15 ist dagegen das Pendant <code>frozendict</code>, das unveränderbare Dictionaries darstellt. fdict = frozendict({"vorname":"Hugo", "nachname":"Meister"}) fset = frozenset({"A", 1, "A"}) print(fdict) print(fdict["vorname"]) print (fset) try: fdict["vorname"] = "Max" print(fdict["vorname"]) except TypeError as e: print(f"Fehler: {e}") try: fset.add("B") print(fset) except AttributeError as e: print(f"Fehler: {e}") Ausgabe: frozendict({'vorname': 'Hugo', 'nachname': 'Meister'}) Hugo frozenset({1, 'A'}) Fehler: 'frozendict' object does not support item assignment Fehler: 'frozenset' object has no attribute 'add' == 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: x = ("a", "b", "c", "d", "e") print(x[2]) print(x[-3]) print(x) print(x[:]) print(x[2:]) print(x[:2]) print(x[::-1]) Ausgabe: c c ('a', 'b', 'c', 'd', 'e') ('a', 'b', 'c', 'd', 'e') ('c', 'd', 'e') ('a', 'b') ('e', 'd', 'c', 'b', 'a') Visualisierung: '''a''' '''b''' '''c''' '''d''' '''e''' +-----------------------------+ | 0 | 1 |//'''2'''//| 3 | 4 | +-----------------------------+ | -5 | -4 |/'''-3'''//| -2 | -1 | +-----------------------------+ |/////////////////////////////| +-----------------------------+ |/////////////''':'''///////////////| +-----------------------------+ | | |///////'''2:'''////////| +-----------------------------+ |////''':2'''/////| | | | +-----------------------------+ |\\\\\<--\\\\\'''::-1'''\\\\<--\\\\\| +-----------------------------+ Beispiel: x = ("a", "b", "c", "d", "e") print(x[2::2]) print(x[2::-1]) print(x[2:0:-1]) Ausgabe: ('c', 'e') ('c', 'b', 'a') ('c', 'b') Hier sieht man sehr schön, dass beim letzen <code>print</code>-Befehl die 0, die in diesem Fall den Buchstaben "a" repräsentiert, als letzer Wert nicht mehr dazugehört (wie weiter oben angedeutet ist der End- oder Stoppwert exklusiv). Will man den Buchstaben "a" dazunehmen, so ist die vorletzte Zeile die richtige. Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH Mit zweidimensionalen Listen/Tupels wird das Ganze schon bedeutend komplexer. Beispiel: Verkettetes Slicing A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] print(A[2::][::2]) Ausgabe: [['20', '21', '22', '23'], ['40', '41', '42', '43']] Das erste Slicing erstellt folgenden Slice: [["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] Das zweite Slicing wird dann auf diesen Slice angewendet und ergibt das genannte Endergebnis. Will man Spalten statt Zeilen, so funktioniert dieser direkte Weg nicht. Man verwendet dann List Comprehensions oder die NumPy-Bibliothek. Diese Bibliothek erlaubt zweidimensionales Slicing. Sie wird erst später bei den Grafiken eingeführt. Deshalb wird hier dazu noch kein Beispiel gezeigt. = 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 (ist der sogenannte Konstruktor). 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 strikt/stark private ist. Wird nur ein Unterstrich verwendet, so deutet dies auf schwach/weakly private hin. Letzteres wird in Python deutlich häufiger verwendet. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. Besonderheiten bei stark und schwach private: * '''stark private''': ** zwei führende Unterstriche ** Name Mangling: Name wird zu <code>_Klassenname__variablenname</code> ** Schutz vor Namenskollisionen bei Vererbung ** am Ende des Namens dürfen keine zwei Unterstriche verwendet werden. Das wären dann Magic Methods oder Dunder-Methods (z.B. __init__). Diese sind public. * '''schwach private''': ** ein führender Unterstrich ** Name bleibt gleich ** ist nur ein Hinweis an die Programmierer, kein starker Schutz ** <code>from modul import *</code>: Objekte mit einem führenden Unterstrich werden standardmäßig nicht importiert ** Standard für die Kapselung bei Python. Schätzung: Weit mehr als 80% der nicht-öffentlichen Attribute/Methoden werden als schwach private deklariert. Weniger als 5-10% als stark private (Quelle: Google KI) == 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 == === Einfachvererbung === [[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 === Mehrfachvererbung === Mit Python ist auch Mehrfachvererbung möglich, z.B.: class A: def _printA(self): print("Klasse A") class B: def _printB(self): print("Klasse B") class C(A,B): def __init__(self): self._printA() self._printB() c = C() Ausgabe: Klasse A Klasse B Die Klasse C erbt von A und B. In UML sieht das in etwa so aus: +-----------------------------+ +-------------------------+ | A | | B | +-----------------------------+ +-------------------------+ | | | | +-----------------------------+ +-------------------------+ | # printA() | | # printB() | +-----------------------------+ +-------------------------+ . . / \ / \ --- --- | | | | +---------------------+ | C | +---------------------+ | | +---------------------+ | | +---------------------+ <code>printA()</code> und <code>printB()</code> sind schwach private (in UML als <code>protected</code> mit einem # dargestellt). Es ist aber zu beachten, dass schwach private nur Konvention ist. Praktisch könnte man auch von außerhalb auf diese Methoden zugreifen. Das soll man aber nicht tun, und das wird in Python durch einen Unterstrich angedeutet. == Statische Attribute, statische Methoden und Klassenmethoden == Beispiel: class Math: pi = 3.14159265 cnt = 0 @staticmethod def expo(): return 2.71828183 @classmethod def counterPlus(cls): cls.cnt += 1 print(Math.pi) print(Math.expo()) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Ausgabe: 3.14159265 2.71828183 1 2 3 Statische Attribute und Methoden, bzw. Klassenmethoden, sind direkt einer Klasse zugeordnet und nicht einem Objekt. Der Unterschied zwischen statischen Methoden und Klassenmethoden ist, dass statische Methoden nicht auf Klassenattribute (bzw. statische Attribute) zugreifen können, Klassenmethoden aber schon. <code>pi</code> und <code>cnt</code> sind statische Attribute. <code>expo()</code> ist eine statische Methode. <code>counterPlus(cls)</code> ist eine Klassenmethode. Die Bezeichnung des Parameters als <code>cls</code> ist reine Konvention, man sollte sich aber daran halten. = 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. Die "arange"-Funktion aus der NumPy-Bibliothek wird später ein bisschen genauer behandelt (siehe [[Ing_Mathematik:_Python#Zahlenfolgen|Zahlenfolgen]]). <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. Die Farbe des Graphen kann man über den <code>color</code>-Schalter einstellen, z.B. auf rot setzen: plt.plot(x, y, color="red") Das ist nur eine Möglichkeit, es gibt auch andere, z.B. mittels * hexadezimaler Zahl, z.B. <code>color="#ff0000"</code> * Kurzform, z.B. <code>"r"</code> Statt <code>color</code> kann auch die Kurzform <code>c</code> verwendet werden. <u>Kleine Aufgabe:</u> Probieren Sie diese "Farbspielereien" am Computer aus. === Graphen mehrerer Funktionen und weiteres === ==== Mehrere Funktionen in einen Plot zeichnen ==== 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]] Einige weitere Bespiele zu <code>ls</code></code>: * - ... durchgezogene Linie * -. ... Strichpunkt-Linie * dashed ... gestrichelte Linie ==== Marker ==== Desweiteren kann man Marker verwenden. Beispiel: import matplotlib.pyplot as plt plt.plot(10, 10, "ob", markersize=30) plt.plot(20,20, ".r") plt.plot(30,30, "Dg", ms=15) plt.plot(40, 40, "ok", ms=20, mec="#FF5733", mew=3) plt.plot(50, 50, "vk", ms=20, mec="yellow", mew=3) plt.grid() plt.show() Das erste Zeichen im String nach den Koordinaten im plot-Befehl symbolisiert die Form (z.B. o für einen Kreis). Das zweite Zeichen steht für die Farbe (z.B. b für blue). <code>markersize</code> oder <code>ms</code> setzt die Größe des Markers. Ausgabe: [[Bild:PythonIng_formatpara1.svg|200px]] Einige weitere Marker: * * ... Stern * + ... Plus * h oder H ... Sechseck Man kann mit den Parametern * <code>markeredgecolor</code> oder kurz <code>mec</code> die Farbe des Markerrandes * <code>markeredgewidth</code> oder kurz <code>mew</code> die Dicke des Randes * <code>markerfacecolor</code> oder kurz <code>mfc</code> die Innenfarbe des Markers bestimmen. === 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]] === Anmerkungen hinzufügen === Anmerkungen (engl. annotations) lassen sich mit matplotlib einfach hinzufügen. Unter Anmerkung wird hier ein Pfeil mit Text verstanden. Beispiel: import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot([1,10], [1,10]) ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0)) plt.show() Ausgabe: [[Datei: PythonIng_ann1.svg|400px]] <code>shrink</code> verkürzt den Pfeil. Sinnvoll sind beim obigen Beispiel Werte irgendwo zwischen 0.0 und 0.3. <u>Kleine Aufgabe:</u> Probieren Sie verschiedene Pfeillängen aus. Die Schriftgröße verändern können Sie übrigens mit <code>fontsize</code> oder <code>size</code>, z.B. ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0), fontsize=25) Eine andere Schreibweise für Dictionaries, die wir weiter oben schon kennengelernt haben, die wir aber nochmals explizit aufschreiben wollen, ist ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops={"facecolor":"red", "shrink":0.0}) === Figure, Axes etc.=== [[Bild:PythonIng_figure33.svg|350px]] * fig (figure) ... das übergeordnete Fenster * ax (axes) ... "Plot", das eigentliche Zeichenfenster * axis ... die Koordinatenachsen * spine ... Begrenzungslinien des Zeichenfensters * tick ... Markierung an den Achsen des Zeichenfensters Beispiel (eine figure mit 6 axes, ax-Titel, y-axis-Titel): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=3, ncols=2) ax[0][0].text(0.1, 0.5, "11") ax[0][1].text(0.1, 0.5, "12") ax[1][0].text(0.1, 0.5, "21") ax[1][1].text(0.1, 0.5, "22") ax[2][0].text(0.1, 0.5, "31") ax[2][1].text(0.1, 0.5, "32") ax[0][0].set(ylabel="y-label", title="axes 1") plt.show() [[Bild:PythonIng_figure34.png|350px]] Beispiel (Achsenbereiche setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-1, 10, 1, 12)) plt.show() [[Bild:PythonIng_figure35.svg|350px]] Beispiel (Gitter einschalten): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-5, 10, 1, 12)) ax[0].minorticks_on() ax[0].grid(which='minor') ax[1].grid(which='major') plt.show() [[Bild:PythonIng_figure36.svg|350px]] Beispiel (ticks setzen) import matplotlib.pyplot as plt import matplotlib.ticker as tick fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") pos = [0, .2, .5] lab = ["a", "b", "c"] ax.xaxis.set_major_locator(tick.FixedLocator(pos)) ax.xaxis.set_major_formatter(tick.FixedFormatter(lab)) plt.show() [[Bild:PythonIng_figure37.svg|350px]] Beispiel (Achsenkreuz beliebig setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") ax.spines['top'].set_color('none') ax.spines['right'].set_color('none') ax.spines['bottom'].set_position(('data', 0.3)) ax.spines['left'].set_position(('data', 0.5)) plt.show() [[Bild:PythonIng_figure38.svg|350px]] Beispiel (Inset): import matplotlib.pyplot as plt # Hauptplot erstellen fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") # Inset hinzu: axin = ax.inset_axes([0.4, 0.4, 0.3, 0.3]) axin.text(0, 0, "Servus") axin.plot() plt.show() [[Bild:PythonIng_figure40.svg|350px]] Beispiel (Hintergrundfarben setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.set_facecolor("green") fig.set_facecolor("yellow") plt.plot([1, 3], [6, 8]) plt.show() [[Bild:PythonIng_figure39.svg|300px]] === GridSpec === Mit figures und axes lassen sich schon jede Menge an Grafikdetails einstellen. GridSpec ist dann der Turbo oder Raketenantrieb den matplotlib eingebaut hat. Naja, vielleicht ist das etwas übertrieben, aber GridSpec ist sozusagen ein flexibler Layout-Manager. Bisher haben wir immer gleich große axes gezeichnet. Mit GridSpec können wir Subplots über mehrere Zeilen und Spalten ziehen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) gs = fig.add_gridspec(3, 3) ax1 = fig.add_subplot(gs[0, :]) ax1.set_title("Bereich 1: gs[0, :]") ax2 = fig.add_subplot(gs[1:, 0]) ax2.set_title("Bereich 2: gs[1:, 0]") ax3 = fig.add_subplot(gs[1, 1:]) ax3.set_title("Bereich 3: gs[1, 1:]") ax4 = fig.add_subplot(gs[2, 1:]) ax4.set_title("Bereich 4: gs[2, 1:]") plt.show() [[Bild:PythonIng_gridspec1.svg|350px]] Erklärung zu <code>(gs[zeile, spalte])</code>: Slicing; damit legen Sie fest, welche Zellen ein Subplot belegt. Achtung! Die Indizes beginnen bei 0 und nicht bei 1 (d.h., die erste Zeile/Spalte hat den Index 0) * : ... alle * 1: ... 1 bis Ende * etc. <code>constrained_layout=True</code> sorgt dafür, dass sich die Subplots nicht überlappen. Aber auch die Spaltenbreiten und Zeilenhöhen lassen sich anpassen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) # Verhältnis erste zu zweite Zeile = 1.5:1 # Verhältnis erste zu zweite Spalte = 1:4 gs = fig.add_gridspec(2, 2, figure=fig, height_ratios=[1.5, 1], width_ratios=[1, 4]) ax1 = fig.add_subplot(gs[0, 0]) ax1.set_title("Bereich 1: gs[0, 0]") ax2 = fig.add_subplot(gs[0, 1]) ax2.set_title("Bereich 2: gs[0, 0]") ax3 = fig.add_subplot(gs[1, 0]) ax3.set_title("Bereich 3: gs[1, 0]") ax4 = fig.add_subplot(gs[1, 1:]) ax4.set_title("Bereich 4: gs[1, 1]") plt.show() [[Bild:PythonIng_gridspec2.svg|350px]] Auch die Ränder lassen sich einstellen. Beispiel 1: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.2, hspace=0.2) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec3.svg|300px]] * hspace ... height space, vertikaler Abstand zwischen den Zeilen * wspace ... width space, horizontaler Abstand zwischen den Spalten Und im Vergleich dazu Beispiel 2: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.3, right=0.7, bottom=0.3, top=0.7, wspace=0.3, hspace=0.3) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec4.svg|300px]] Es gibt noch einige weitere Details, die sich mit GridSpec variieren lassen (z.B. SubGridSpec ... verschachtelte Grid-Layouts). Aber wir belassen es vorerst dabei. Das bisher Gezeigte sollte für die meisten Probleme ausreichend sein. === 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|300px|center]] * 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|300px|center]] * 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. * Zeichnen Sie folgendes Bild (die Achsenbeschriftungen positionieren Sie z.B. mit den Befehlen <code>ax.set_xlabel("x", loc="right"); ax.set_ylabel("y", loc="top")</code>): [[Bild:PythonIng_aufgabe24.svg|400px|center]] * Zeichnen Sie mit Python, matplotlib und NumPy folgendes Bild: [[Bild:PythonIng_figure41.svg|400px|center]] * Zeichnen Sie mit GridSpec ein Layout, das in etwa wie folgendes aussieht (<code>height_ratios=[1, 2], width_ratios=[2, 1]</code>): [[Bild:PythonIng_aufgabe23.svg|300px|center]] == 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 und fortgeschrittenes 3D == === 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 build123d === Hiermit lassen sich einfach 3D-Modelle für CAD-Systeme erstellen. [https://build123d.readthedocs.io/en/latest/ build123d] ist ein externes Modul und muss vorab, wie schon für mehrere andere Module gezeigt, installiert werden (pip, venv etc.). Beispielcode: from build123d import * # Bauteil with BuildPart() as bauteil: # Zylinder erstellen Cylinder(100, 60) # Bohrung with BuildSketch(bauteil.faces().sort_by(Axis.Z)[-1]) as loch: Circle(radius=10) # negativ extrudieren extrude(amount=-60, mode=Mode.SUBTRACT) # Dem Bauteil z.B. die Farbe rot zuweisen # bauteil.part.color = Color("red", alpha=1.0) # Fertiges Modell exportieren export_step(bauteil.part, "bauteil.step") Ausgabe ({{W|Standard for the exchange of product model data | STEP}}-Datei, z.B. in {{W|FreeCAD}} importiert, Screenshot): [[Datei:PythonIng_build123d.jpg | 300px]] FreeCAD ist ein freies 3D-CAD-System. Die [[Ing_Mathematik:_Python#FreeCAD | Makroprogrammierung]] in diesem System wird später noch gezeigt. STEP steht für "'''St'''andard for the '''e'''xchange of '''p'''roduct model data" und ist ein Datenaustauschformat. === 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> == Sonstige Grafikbibliotheken == === Py5 === Im Buch ''Iacono et al.: Programmieren trainieren, mit über 150 Workouts in Java und Python; 3. Aufl., Hanser, 2023'' werden zahlreiche Aufgaben gestellt, die u.a. mit Processing zu lösen sind. Processing ist eine einfache Programmiersprache und Entwicklungsumgebung. Warum ich das erwähne? Weil in älteren Processing-Versionen auch die Programmiersprache Python 2 abgedeckt war. Bei neueren Versionen gibt es keine Python-Anbindung mehr (getestet nur mit openSUSE Tumbleweed und Python 3). Somit funktioniert der Python-Teil im genannten Buch zum Teil nicht mehr. Ist das tragisch, oder kann das Buch weggeschmissen werden? Nein! Es gibt Alternativen zu Processing. Zum einen die im Buch selbst genannten Bibliotheken tkinter oder pygame. Pygame wird dort auch kurz anhand eines Beispiels thematisiert. Aber noch besser ist es, die py5-Bibliothek zu verwenden (lt. Google KI vom 30. Sept. 2026: ''Der echte, moderne „spirituelle Nachfolger“''). Damit ist die Funktionalität von Processing gewährleistet. Man kann aber beliebige Editoren oder IDEs zur Programmerstellung verwenden, ohne an Processing als IDE gebunden zu sein. Nachfolgend werden ein paar Beispiele zu dieser externen Bibliothek gegeben. Der erste Schritt ist wieder, die py5-Bibliothek zu installieren (z.B. mittels pip in eine virtuelle Umgebung; Python>=3.10 und ein JDK>=17 müssen auch installiert sein (Stand Okt. 2026)). Dann kann es schon mit dem Programmieren losgehen. Beispiel (angelehnt an die ''Aufgabe W.2.3 Haus'' des genannten Buchs, jedoch etwas erweitert): import py5 def setup(): py5.size(600, 600) # Fenstergröße 600px x 600px py5.background(255, 255, 255) # Hintergrundfarbe ... weiss def draw(): # Haus zeichnen py5.fill(255, 255, 255) # Hintergrundfarbe ... weiss py5.stroke(255, 0, 0) # Linienfarbe ... rot py5.stroke_weight(3) # Linienbreite ... 3px py5.rect(150, 200, 300, 300) # Rechteck zeichnen (x, y, b, h) py5.triangle(150, 200, 300, 100, 450, 200) # Dreieck zeichnen py5.circle(250, 300, 70) # Kreis zeichnen (x, y, d) # Bemassung zeichnen py5.stroke(0, 255, 0) py5.stroke_weight(1) py5.line(300, 100, 550, 100) # Linie zeichnen py5.line(450, 200, 550, 200) py5.line(450, 500, 550, 500) py5.line(150, 500, 150, 550) py5.line(450, 500, 450, 550) py5.text_size(20) # Textgroesse py5.fill(0, 255, 0) # Textfarbe py5.text("300", 280, 550) # Text zeichnen py5.text("100", 500, 150) py5.text("300", 500, 350) # Gedrehtes Rechteck zeichnen py5.stroke(255, 255, 0) py5.stroke_weight(3) py5.fill(200, 200, 200) py5.translate(320, 260) py5.rotate(0.3) # Drehung (in Radiant) py5.rect(0, 0, 100, 50) # Startet den Sketch py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_1.png|300px]] Das skizzierte Haus ist mit einer rudimentären Bemaßung versehen und hat zwei Fenster, ein rundes und ein gedrehtes, trübes rechteckiges. Beispiel (Mausoperationen): import py5 def setup(): py5.size(600, 600) py5.background(255, 255, 255) def draw(): pass def mouse_pressed(): py5.text_size(15) py5.fill(0, 0, 255) x = py5.mouse_x y = py5.mouse_y py5.text("(%d, %d)" %(x, y), x, y) # Positionsdaten bei Maus-Klick schreiben py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_2.png|300px]] Obiges Bild zeigt das Resultat nach einigen Mausklicks. Man erkennt, dass links-oben der Koordinatennullpunkt ist. Die x-Achse zeigt nach rechts, die y-Achse nach unten. Beispiel (Fünfeck): import py5 import random def setup(): py5.size(400, 400) py5.no_loop() # statisches Bild (schont die Ressourcen) def draw(): py5.translate(py5.width / 2, py5.height / 2) # KS setzen radius = 150 eck_anzahl = 5 py5.fill(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # Füllfarbe auf Zufallswert setzen py5.stroke(155, 0, 0) py5.stroke_weight(5) # Fünfeck als Polygon zeichnen py5.begin_shape() for i in range(eck_anzahl): winkel = i * py5.TWO_PI / eck_anzahl - py5.HALF_PI x = py5.cos(winkel) * radius y = py5.sin(winkel) * radius py5.vertex(x, y) py5.end_shape(py5.CLOSE) py5.run_sketch() Ausgabe, bspw.: [[Datei:PythonIng_py5_5.png|200px]] <u>Kleine Aufgaben:</u> * Um die Funktionsweise von <code>py5.no_loop()</code> zu verstehen, kommentieren Sie die Anweisung im Beispiel einfach aus, und starten Sie das Programm erneut. * Zeichnen Sie ein Siebeneck. * Was passiert, wenn man <code>py5.CLOSE</code> im <code>py5.end_shape()</code>-Befehl weglässt? Beispiel (Grafik laden und zeichnen): import py5 image1 = None def setup(): global image1 py5.size(600, 600) py5.no_loop() image1 = py5.load_image("PythonIng_py5_1.png") def draw(): py5.image(image1, 0, 0) py5.run_sketch() Hier wird einfach das Bild vom Haus-Beispiel (Screenshot von weiter oben) geladen und gezeichnet. Beispiel (angelehnt an ''W.5.10 Wurfparabel'' des genannten Buches; mit OOP erstellt): import py5 class Wurfparabel(py5.Sketch): def __init__(self, v0, angle): super().__init__() self.v0 = v0 self.angle = self.radians(angle) self.g = 9.81 self.t = 0.0 self.scale = 1.5 def settings(self): self.size(800, 600) def setup(self): self.background(0, 0, 0) def draw(self): self.t += 0.1 self.translate(50, 500) # KS Zeichnen self.fill(255, 255, 0) self.stroke(255, 255, 0) self.stroke_weight(1) self.line(0, 0, 0, -300) self.line(0, 0, 700, 0) # Punkt zeichnen cx = self.v0 * self.cos(self.angle) * self.t * self.scale cy = -(self.v0 * self.sin(self.angle) * self.t - \ self.g/2 * self.t**2) * self.scale self.fill(255, 0, 0) self.stroke(255, 0, 0) self.circle(cx, cy, 3) # Stoppen bei Bodenkontakt if cy >= -5: self.no_loop() def wurfparabel(v0=50, angle=45): sketch = Wurfparabel(v0, angle) sketch.run_sketch() v0 = float(input("v0 = ")) winkel = float(input("winkel = ")) wurfparabel(v0=v0, angle=winkel) Eingabe auf der Konsole: v0 = 60 winkel = 45 Ausgabe: [[Datei:PythonIng_py5_13.png|200px]] Die benötigten Formeln der Bewegungsgleichung kann man leicht herleiten, bzw. man benutzt folgende Grafik als Hilfestellung: [[Datei:Wurfparabel_Zusammenfassung_aktualisierung.png|700px]] <u>Aufgabe:</u> Berechnen Sie den Scheitelpunkt und die Wurfweite bei gegebener Anfangsgeschwindigkeit und gegebenem Abwurfwinkel. Die Abwurfhöhe <math>y_0</math> sei 0. Der Wurf soll auf der Erde stattfinden (g = 9.81 m/s²). Die dazu benötigten Formeln können aus obiger Grafik entnommen werden (oder Sie leiten sie selbst her). Geben Sie die Ergebnisse auf der Konsole aus. Beispiel (eine einfache Animation): import py5 i = 0 def setup(): py5.size(600, 600) py5.frame_rate(5) py5.stroke_weight(20) def draw(): global i py5.background(255, 255, 255) py5.translate(py5.width/2, py5.height/2) py5.rotate(py5.PI/8 * i) py5.line(-100, 0, 100, 0) i += 1 py5.run_sketch() Es wird eine rotierende Linie gezeichnet (z.B. wie bei einem Installationsprozess verwendet - zeigt an, dass der Installationsfortschritt nicht eingefroren ist). Hierbei ist nur aufzupassen, dass der Hintergrund in <code>draw()</code> gesetzt wird, ansonsten würden die Linien additiv gezeichnet. Das ist zwar auch schön, aber eben nicht das gewünschte Resultat. Hiermit belassen wir es mit der py5-Bibliothek und wenden uns wieder "mathematischeren" Themen zu. = 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> standardmäßig aber sehr wohl. Jedoch kann man das bei <code>linspace</code> auch ändern. Dann ist der Stoppwert nicht mehr enthalten: linspace(start, stop, num, endpoint=False) == 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 === Beispiel: 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 Visualisierung: 0 1 2 +-----------+ 0 | 1 | 2 | 3 | +-----------+ 1 | 4 | 5 | '''6''' | +-----------+ Beispiel: import numpy as np A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] B = np.matrix(A) print(B[2::, ::2]) Ausgabe: [['20' '22'] ['30' '32'] ['40' '42']] Mit der NumPy-Bibliothek funktioniert das zweidimensionale Slicing. Mit normalen Listen/Tupels geht das nicht, was wir schon früher thematisiert haben. === 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]. Beispiel: import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: <Compressed Sparse Row sparse array of dtype 'float64' with 5 stored elements and shape (5, 5)> Coords Values (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 csr steht für '''c'''ompressed '''s'''parse '''r'''ow. Andere Möglichkeiten: * coo-Format ('''coo'''rdinate Format): <code>scipy.sparse.coo_array()</code> * lil-Format ('''li'''st of '''l'''ists): <code>scipy.sparse.lil_array()</code> * csc-Format ('''c'''ompressed '''s'''parse '''c'''olumn): <code>scipy.sparse.csc_array()</code> Es gibt auch <code>scipy.sparse.csr_matrix()</code> etc. Diese Befehle sind aber veraltet und es wird empfohlen, statt dessen die <code>..._array()</code>-Funktionen zu verwenden. <u>Aufgaben:</u> Recherchieren Sie im Internet * generell zum Thema "spärlich besetzte Matrizen mit Python". * Was sind die Vor-/Nachteile von csr-arrays gegenüber coo- und lil-arrays? * Wann verwendet man sinnvollerweise csr- und wann csc-arrays? * Wie wandelt man coo-arrays in csr-arrays um? = 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.] Siehe dazu auch das Kapitel [[Ing Mathematik: Numerisches Lösen von linearen Gleichungssystemen]]. Dort wird dieses Themengebiet u.a. mit weitergehenden Python-Skripten vertieft. == 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") <u>Aufgaben</u>: Ermitteln Sie mittels SymPy die <math>\mathcal{L}</math>-Transformierten und dann wieder die <math>\mathcal{L}^{-1}</math>-Transformationen (ähnlich wie beim obigen Beispiel): * <math>\sin(t) \circ\! -\! \bullet \ ?</math> * <math>t^3 \circ\! -\! \bullet \ ?</math> * <math>\text{e}^{\omega t} \circ\! -\! \bullet \ ?</math> =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 [https://mpmath.org/ 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). = pint - mit physikalischen Größen rechnen = [[File:Pint-Glas_UK.jpg|right|100px]] pint ist ein externes Python-Paket zur Manipulation von physikalischen Einheiten. Es muss vor der ersten Benutzung installiert werden (Vorgehensweise wie immer: pip, YaST, via venv etc.). Beispiel: import pint import numpy as np ureg = pint.UnitRegistry() # Einfache Berechnung print(3 * ureg.meter + 4 * ureg.cm) # Mit NumPy npeinzel = [3, 4] * ureg.meter + [2, 5] * ureg.cm print(npeinzel) npsum = np.sum(npeinzel) print(npsum) # Konvertierung von Einheiten npsum_inch = npsum.to(ureg.inch) print(npsum_inch) print(npeinzel.magnitude) print(npeinzel.units) print(npeinzel.dimensionality) Ausgabe: 3.04 meter [3.02 4.05] meter 7.07 meter 278.3464566929134 inch [3.02 4.05] meter [length] Die Dokumentation zu pint findet sich auf [https://pint.readthedocs.io/en/stable/]. =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. <small>Je größer die Anzahl der Datensätze n wird, umso weniger spielt das eine Rolle.</small> == 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. = Pandas kurz erklärt = Hier wird kurz auf das externe Modul Pandas eingegangen. Pandas steht für '''Pan'''el '''Da'''ta und ist ein Modul für die Datenanalyse und -manipulation. == Series == Vorstellbar ist eine "Series" wie eine einzelne Spalte in einem Tabellenkalkulationsprogramm. Beispiel (Index implizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) # Temperaturwerte print(temp) Ausgabe: 0 8.5 1 11.0 2 12.5 3 10.0 4 13.0 5 11.0 6 14.5 dtype: float64 Das Kürzel <code>pd</code> ist Konvention. Es seien Temperaturwerte gegeben. Die erste Spalte bei der Ausgabe ist der Index. Die zweite Spalte enthält die Werte. Beispiel (separater Zugriff auf den Index und auf die Werte): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) print(temp.index) print(temp.values) Ausgabe: RangeIndex(start=0, stop=7, step=1) [ 8.5 11. 12.5 10. 13. 11. 14.5] Beispiel (Index explizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) print("*** Die gesamte Series ***") print(temp) print("*** Temperatur am Donnerstag ***") print(temp["Do"]) print("*** Tage wärmer als 12 Grad ***") temp_gr_12 = temp[temp > 12] print(temp_gr_12) Ausgabe: *** Die gesamte Series *** Mo 8.5 Di 11.0 Mi 12.5 Do 10.0 Fr 13.0 Sa 11.0 So 14.5 dtype: float64 *** Temperatur am Donnerstag *** 10.0 *** Tage wärmer als 12 Grad *** Mi 12.5 Fr 13.0 So 14.5 dtype: float64 == DataFrame == Ein DataFrame entspricht einer Tabelle. Beispiel (Erzeugen eines DataFrames): import pandas as pd tag = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] v1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=tag) v2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=tag) v3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=tag) tab = pd.concat([v1, v2, v3], axis=1) tab.columns = stadt print(tab) print(type(tab)) Ausgabe: Linz Salzburg Innsbruck Mo 1 8 Hallo Di 2 9 16 Mi 3 10 17 Do 4 11 18 Fr 5 12 19 Sa 6 13 20 So 7 14 21 <class 'pandas.core.frame.DataFrame'> Wir haben hier drei Datenspalten (v1, v2, v3) mit den Spaltennamen "Linz", "Salzburg" und "Innsbruck", sowie sieben Zeilen "Mo" bis "So". Beispiel (Zugriff auf eine Spalte): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab["Salzburg"]) Ausgabe: Mo 8 Di 9 Mi 10 Do 11 Fr 12 Sa 13 So 14 Name: Salzburg, dtype: int64 Beispiel (Zugriff auf eine Zeile): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab.loc["Di"]) Ausgabe: Linz 2 Salzburg 9 Innsbruck 16 Name: Di, dtype: object == Grafiken erstellen == Hier sei nur ein Torten- oder Kreisdiagramm gezeichnet: import pandas as pd import matplotlib.pyplot as plt temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) expl = [0, 0.2, 0, 0, 0, 0, 0] temp.plot.pie(figsize=(5,5), explode=expl) plt.show() Ausgabe: [[Datei:IngPython_pandas1.svg|350px]] Die <code>figsize</code> hat nichts mit der Spaltenanzahl zu tun, sondern setzt die Fenster- oder Diagrammgröße in Inches (Breite * Höhe). Mit Pandas kann man auch noch andere Diagrammarten erstellen, z.B. Linien- oder Balkendiagramme. Dies wird hier aber nicht vorgeführt. == Arbeiten mit CSV- und LibreOffice-Dateien == Gegeben sei eine CSV-Datei test1.csv: Mai;Juni;Juli 12;17;45 15;65;65 16;32;23 4;14;7 23;17;Keine Daten verfügbar 67;Keine Daten verfügbar;45 15;24;12 Lesen von dieser CSV-Datei: import pandas as pd tab = pd.read_csv("test1.csv", sep=";") print(tab) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Schreiben in eine CSV-Datei test2.csv: import pandas as pd val = pd.read_csv("test1.csv", sep=";") val.to_csv("test2.csv", index=False) Ausgabedatei: Mai,Juni,Juli 12,17,45 15,65,65 16,32,23 4,14,7 23,17,Keine Daten verfügbar 67,Keine Daten verfügbar,45 15,24,12 Die Ausgabedatei ist beinahe gleich wie die Eingabedatei. Es werden aber Kommas statt Strichpunkten verwendet. Daher kommt auch der Name CSV ('''C'''omma '''S'''eparated '''V'''alue). Will man wieder Strichpunkte, so kann man dies über den Parameter <code>sep=";"</code> in der <code>to_csv</code>-Funktion machen. Um direkt von einer ODS-Datei (LibreOffice Calc, '''O'''pen '''D'''ocument '''S'''preadsheet) zu lesen muss das Modul odfpy extra installiert sein (per YaST, pip etc.). Gelesen wird dann folgendermaßen: import pandas as pd df = pd.read_excel('test1.ods', engine='odf') print(df) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Die Datei "test1.ods" sei die Datei, aus der das obige "test1.csv" generiert wurde. Schreiben kann man ebenfalls in eine ODS-Datei: import pandas as pd daten = { "Name": ["Hugo", "Richie", "Wilhelmine"], "Alter": [38, 30, 29], "Ort": ["Linz", "Innsbruck", "Salzburg"] } tab = pd.DataFrame(daten) with pd.ExcelWriter("test2.ods", engine="odf") as writer: tab.to_excel(writer, sheet_name="Daten", index=False) Es ergibt sich eine "test2.ods"-Datei (Screenshot): [[Datei:IngPython_pandas2.png|250px]] == Sonstiges == Hier seien stichpunktartig einige weitere Features von Pandas angeführt: * {{W|Pivot-Tabelle|Pivot-Tabellen}} (pivot ... Dreh-, Angelpunkt): Zweck - große Datenmengen schnell zusammenfassen, filtern und analysieren. * NaN ('''n'''ot '''a''' '''n'''umber): In einem Datensatz fehlt ein Wert oder ist nicht definiert. * groupby-Funktion: Daten strukturieren, aggregieren oder Zwischensummen bilden. * {{W|Binning#Datenverarbeitung_und_-analyse|Binning}}: Klassenbildung. * Mehrstufige Indizierung (Hierarchical Indexing oder MultiIndex) * Zeitreihen (Time Series) Für tiefergehende und weiterführende Informationen wird auf das Buch von ''Klein: Numerisches Python; Hanser, 2023'' und [https://pandas.pydata.org/ pandas] verwiesen. = 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) Das funktioniert übrigens nicht nur bei Funktionsannotationen, sondern auch mit Variablen etc. from typing import Final # "Konstante", ab Python 3.8 NUMBER: Final = 123 print(NUMBER) NUMBER = 125 print(NUMBER) # Variable a: int = 345 print(a) a = "Irgendwas" print(a) # Variable (Pipe-Symbol), ab Python 3.10 b : int|str = 567 print(b) b = "sonstiges" print(b) Ausgabe mit python: 123 125 345 Irgendwas 567 sonstiges Ausgabe mit mypy: test3.py:6: error: Cannot assign to final name "NUMBER" [misc] test3.py:12: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] Found 2 errors in 1 file (checked 1 source file) Python kennt übrigens keine Konstanten. Man kann diese mittels Großbuchstaben andeuten. Das ist aber nur Konvention, hat aber keine sonstigen Auswirkungen. Oder man verwendet wie oben gezeigt <code>Final</code>. Dann werfen Typchecker Errors aus. ==pydoc== pydoc ist ein Werkzeug, um Python-Module, -Funktionen und -Klassen zu dokumentieren. Beispiel <code>test1.py</code>: """Rechen-Modul. Funktionen die von pydoc gelesen werden. """ def multiply(a, b): """Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a * b def add(a, b): """Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a + b if __name__ == '__main__': print(multiply(3, 5)) print(add(4, 6)) Ausgabe mit <code>python3.13 -m pydoc test1</code> auf der Konsole: Help on module test1: NAME test1 - Rechen-Modul. DESCRIPTION Funktionen die von pydoc gelesen werden. FUNCTIONS add(a, b) Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl multiply(a, b) Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl FILE /home/xyz/tmp/test1.py Siehe zu diesem Thema z.B. auch [https://docs.python.org/3/library/pydoc.html]. Mittels <code>python3.13 -m pydoc -w test1</code> kann man auch eine HTML-Dokumentation erstellen. Wofür <code>if __name__ == '__main__':</code> steht, siehe z.B. etwas später im Abschnitt [[Ing_Mathematik:_Python#Doctests]]. == 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.3 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.5.1 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://build123d.readthedocs.io/en/latest/ build123d] * [https://docs.vtk.org/en/latest/api/python.html VTK] * [https://docs.bokeh.org/en/latest/docs/first_steps.html bokeh] * [https://py5coding.org/ py5] == 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] * [https://pint.readthedocs.io/en/stable/ pint] = 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] * Iacono et al.: Programmieren trainieren; 3. Aufl., Hanser, 2023, ISBN 978-3-446-47766-7 * 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}} pv6fr10xon7ocmy149ccpuzs0pn012e 1092060 1092059 2026-10-10T15:19:23Z Intruder 1513 /* Strings und Platzhalter */ 1092060 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 (welche Verzeichnisse im PATH liegen, kann bspw. mit <code>echo $PATH</code> abgefragt werden). 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 == === Tests === 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) === Popularität === * An Beliebtheit rangiert Python mit Stand September 2026 mit einem Rating von 17,76% 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 Q1/2026 auf Rang 2, vor TypeScript und hinter JavaScript. * [https://pypl.github.io/PYPL.html PYPL] (Popularity of Programming Language Index) ist ein anderer Popularitätsindex für Programmiersprachen. Auch hier liegt Python mit großem Vorsprung auf dem ersten Platz (52,08%), gefolgt von Java (13,34%) und C/C++ (7,99%) (Stand September 2026). === Namensgebung === 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 in PyConUS24.jpg|{{W|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 <code>help</code> oder <code>help()</code> 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, bzw. der Eingabe von q, quit oder exit mit anschließendem Drücken der RETURN-Taste. == 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 Theoretisch kann man auch Umlaute, griechische Buchstaben u.ä. in Variablenbezeichnern nutzen, z.B. ößÜ = 16 α = 134 print(ößÜ) print(α) Ausgabe: 16 134 Davon wird aber aus offensichtlichen Gründen ({{W|Englisch als Lingua franca}}) abgeraten. Das Eurozeichen (€) etc. darf man ohnehin nicht in Variablenbezeichnern verwenden, dies ergäbe einen Syntaxfehler. Siehe dazu auch [https://peps.python.org/pep-3131/]. 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 f-Strings (ab Python 3.6) sind die modernere Variante gegenüber %-Strings. Sie sind in der Regel auch schneller. 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. Man beachte, dass {{W|Rundungsfehler}} auftreten können. D.h. man sollte bei der Verwendung von {{W|Gleitkommazahl|Gleitpunktzahlen}} niemals auf absolute Genauigkeit prüfen, sondern eine Toleranz (einen <math>\varepsilon</math>-Wert) vorgeben. Ein klassisches Beispiel, das ein falsches Ergebnis liefert, ist: i = 0.1 + 0.2 print(i) if i == 0.3: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 NOK Und das, obwohl man schon in der Volksschule lernt, dass 0.1 + 0.2 gleich 0.3 ist. Korrekt muss das obige Beispiel z.B. so umgeschrieben werden: ist = 0.1 + 0.2 soll = 0.3 epsilon = 0.000001 print(ist) if abs(ist-soll) < epsilon: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 OK Alternativ kann auch die <code>math.isclose()</code>-Funktion verwendet werden. Das sei aber hier nur erwähnt und nicht konkret ausgeführt. <u>Aufgabe:</u> Recherchieren Sie im Internet zum Thema <code>math.isclose()</code>-Funktion. === 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 == Kopfgesteuerte Schleife, Endlosschleife und fußgesteuerte Schleife: [[File:Programmingloops.svg|450px]] === 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 Der <code>break</code>-Befehl wird etwas später noch einmal kurz thematisiert. === 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!") print("Setze Programm fort!") Ausgabe: Das Ergebnis ist unendlich Setze Programm fort! 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). Danach wird das Programm fortgesetzt. 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. Neu in Python 3.14 ist, dass mehrere Ausnahmen auch ohne Klammern angegeben werden können, z.B.: bis inkl. Python 3.13 nur: except (ZeroDivisionError, ValueError): ab Python 3.14 auch: except ZeroDivisionError, ValueError: == 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 c, d = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(c) print(d) Ausgabe: 8 15 Es werden r1 und r2 zurückgegeben und den Variablen c und d zugewiesen. (r1, r2) und (c, d) sind Tupel. Was Tupel konkret sind, siehe etwas später in diesem Kapitel. 3 und 5 sind die aktuellen Parameter (die Argumente). a und b sind die formalen Parameter. Die Zuweisung der aktuellen zu den formalen Parametern erfolgt in der gegebenen Reihenfolge, also die Zahl 3 wird an a übergeben und die Zahl 5 an b. Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 c, d = mathFunc(3, 5) print(c) print(d) e, f = mathFunc(5) print(e) print(f) g, h = mathFunc(b=6) print(g) print(h) Ausgabe: 8 15 25 100 16 60 Erklärung: * Erster <code>mathFunc</code>-Aufruf: Es werden, wie weiter oben, die Argumente 3 und 5 an die formalen Parameter a und b übergeben. Die Rückgabewerte r1 und r2 werden an c und d übergeben. Somit ist c=8 und d=15. * Zweiter <code>mathFunc</code>-Aufruf: Es wird nur das Argment 5 an a übergeben. b behält den Vorgabewert 20. D.h. e= 25 und f=100. * Dritter <code>mathFunc</code>-Aufruf: Es wird nur die Zahl 6 an b übergeben, a behält seinen Vorgabewert 10. Somit ergibt sich g=16 und h=60. Beispiel (Schlüsselwortargumente vs. Positionsargumente): def mathFunc(a, b): r1 = a - b r2 = a / b return r1, r2 c, d = mathFunc(b=3, a=5) # Schlüsselwortargumente e, f = mathFunc(3, 5) # Positionsargumente print(c) print(d) print(e) print(f) Ausgabe: 2 1.6666666666666667 -2 0.6 Durch die Verwendung von Schlüsselwortargumenten können die Argumente in beliebiger Reihenfolge angegeben werden. Bei den Positionsargumenten ist die Reihenfolge vorgegeben. Daraus resultiert auch der Unterschied bei den obigen Ausgaben. Werden beide Arten in einem Aufruf gemischt, so müssen die Positionsargumente zuerst angegeben werden. Den Unterschied zwischen beiden Argumentarten zu kennen ist wichtig, nicht nur bei selbst erstellten Funktionen. Auch beim Aufruf von Python-Funktionen verfolgt einen die Mischung von Schlüsselwort- und Positionsargumenten auf Schritt und Tritt, z.B. beim etwas später folgenden Abschnitt über "Grafiken zeichnen": plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") Bei dieser <code>plot</code>-Funktion aus dem Modul <code>matplotlib.pyplot</code> sind <code>x</code> und <code>y1</code> Positionsargumente. <code>label, lw</code> und <code>ls</code> sind die Namen der Schlüsselwortargumente. Die Schlüsselwortargumente könnten auch in anderer Reihenfolge angeschrieben werden, nicht jedoch die Positionsargumente! Siehe zu <code>matplotlib.pyplot.plot</code> auch die Seite [https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html]. == 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 und Listen == 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. 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. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. # 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 (eine Liste von Wörtern zu einem einzigen zusammenhängenden Text verbinden): woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt == Dictionaries und Mengen == 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'> Leere Mengen lassen sich mit <code>set()</code> erzeugen: menge = set() # leere Menge print(menge) menge.add("Hallo") # Element hinzufügen print(menge) Ausgabe: set() {'Hallo'} Beispiel (einige Mengenoperationen): mengeA = {"Hallo", "Welt"} mengeB = {1, 2, "Welt", 3} # Vereinigung print(mengeA | mengeB) print(mengeA.union(mengeB)) # Durchschnitt print(mengeA & mengeB) print(mengeA.intersection(mengeB)) # Differenz print(mengeA - mengeB) print(mengeA.difference(mengeB)) # Symmetrische Differenz print(mengeA ^ mengeB) print(mengeA.symmetric_difference(mengeB)) # Teilmenge print(mengeA.issubset(mengeB)) # Mehrere Elemente hinzu mengeA.update(["Super", 67, 125]) print(mengeA) # Element löschen mengeA.discard("Hallo") print(mengeA) Ausgabe: {1, 2, 3, 'Welt', 'Hallo'} {1, 2, 3, 'Welt', 'Hallo'} {'Welt'} {'Welt'} {'Hallo'} {'Hallo'} {1, 2, 3, 'Hallo'} {1, 2, 3, 'Hallo'} False {67, 'Super', 'Welt', 'Hallo', 125} {67, 'Super', 'Welt', 125} Es gibt schon seit "ewigen" Zeiten unveränderbare Mengen, die mittels <code>frozenset</code> eingeleitet werden. Neu ab Python 3.15 ist dagegen das Pendant <code>frozendict</code>, das unveränderbare Dictionaries darstellt. fdict = frozendict({"vorname":"Hugo", "nachname":"Meister"}) fset = frozenset({"A", 1, "A"}) print(fdict) print(fdict["vorname"]) print (fset) try: fdict["vorname"] = "Max" print(fdict["vorname"]) except TypeError as e: print(f"Fehler: {e}") try: fset.add("B") print(fset) except AttributeError as e: print(f"Fehler: {e}") Ausgabe: frozendict({'vorname': 'Hugo', 'nachname': 'Meister'}) Hugo frozenset({1, 'A'}) Fehler: 'frozendict' object does not support item assignment Fehler: 'frozenset' object has no attribute 'add' == 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: x = ("a", "b", "c", "d", "e") print(x[2]) print(x[-3]) print(x) print(x[:]) print(x[2:]) print(x[:2]) print(x[::-1]) Ausgabe: c c ('a', 'b', 'c', 'd', 'e') ('a', 'b', 'c', 'd', 'e') ('c', 'd', 'e') ('a', 'b') ('e', 'd', 'c', 'b', 'a') Visualisierung: '''a''' '''b''' '''c''' '''d''' '''e''' +-----------------------------+ | 0 | 1 |//'''2'''//| 3 | 4 | +-----------------------------+ | -5 | -4 |/'''-3'''//| -2 | -1 | +-----------------------------+ |/////////////////////////////| +-----------------------------+ |/////////////''':'''///////////////| +-----------------------------+ | | |///////'''2:'''////////| +-----------------------------+ |////''':2'''/////| | | | +-----------------------------+ |\\\\\<--\\\\\'''::-1'''\\\\<--\\\\\| +-----------------------------+ Beispiel: x = ("a", "b", "c", "d", "e") print(x[2::2]) print(x[2::-1]) print(x[2:0:-1]) Ausgabe: ('c', 'e') ('c', 'b', 'a') ('c', 'b') Hier sieht man sehr schön, dass beim letzen <code>print</code>-Befehl die 0, die in diesem Fall den Buchstaben "a" repräsentiert, als letzer Wert nicht mehr dazugehört (wie weiter oben angedeutet ist der End- oder Stoppwert exklusiv). Will man den Buchstaben "a" dazunehmen, so ist die vorletzte Zeile die richtige. Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH Mit zweidimensionalen Listen/Tupels wird das Ganze schon bedeutend komplexer. Beispiel: Verkettetes Slicing A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] print(A[2::][::2]) Ausgabe: [['20', '21', '22', '23'], ['40', '41', '42', '43']] Das erste Slicing erstellt folgenden Slice: [["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] Das zweite Slicing wird dann auf diesen Slice angewendet und ergibt das genannte Endergebnis. Will man Spalten statt Zeilen, so funktioniert dieser direkte Weg nicht. Man verwendet dann List Comprehensions oder die NumPy-Bibliothek. Diese Bibliothek erlaubt zweidimensionales Slicing. Sie wird erst später bei den Grafiken eingeführt. Deshalb wird hier dazu noch kein Beispiel gezeigt. = 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 (ist der sogenannte Konstruktor). 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 strikt/stark private ist. Wird nur ein Unterstrich verwendet, so deutet dies auf schwach/weakly private hin. Letzteres wird in Python deutlich häufiger verwendet. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. Besonderheiten bei stark und schwach private: * '''stark private''': ** zwei führende Unterstriche ** Name Mangling: Name wird zu <code>_Klassenname__variablenname</code> ** Schutz vor Namenskollisionen bei Vererbung ** am Ende des Namens dürfen keine zwei Unterstriche verwendet werden. Das wären dann Magic Methods oder Dunder-Methods (z.B. __init__). Diese sind public. * '''schwach private''': ** ein führender Unterstrich ** Name bleibt gleich ** ist nur ein Hinweis an die Programmierer, kein starker Schutz ** <code>from modul import *</code>: Objekte mit einem führenden Unterstrich werden standardmäßig nicht importiert ** Standard für die Kapselung bei Python. Schätzung: Weit mehr als 80% der nicht-öffentlichen Attribute/Methoden werden als schwach private deklariert. Weniger als 5-10% als stark private (Quelle: Google KI) == 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 == === Einfachvererbung === [[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 === Mehrfachvererbung === Mit Python ist auch Mehrfachvererbung möglich, z.B.: class A: def _printA(self): print("Klasse A") class B: def _printB(self): print("Klasse B") class C(A,B): def __init__(self): self._printA() self._printB() c = C() Ausgabe: Klasse A Klasse B Die Klasse C erbt von A und B. In UML sieht das in etwa so aus: +-----------------------------+ +-------------------------+ | A | | B | +-----------------------------+ +-------------------------+ | | | | +-----------------------------+ +-------------------------+ | # printA() | | # printB() | +-----------------------------+ +-------------------------+ . . / \ / \ --- --- | | | | +---------------------+ | C | +---------------------+ | | +---------------------+ | | +---------------------+ <code>printA()</code> und <code>printB()</code> sind schwach private (in UML als <code>protected</code> mit einem # dargestellt). Es ist aber zu beachten, dass schwach private nur Konvention ist. Praktisch könnte man auch von außerhalb auf diese Methoden zugreifen. Das soll man aber nicht tun, und das wird in Python durch einen Unterstrich angedeutet. == Statische Attribute, statische Methoden und Klassenmethoden == Beispiel: class Math: pi = 3.14159265 cnt = 0 @staticmethod def expo(): return 2.71828183 @classmethod def counterPlus(cls): cls.cnt += 1 print(Math.pi) print(Math.expo()) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Ausgabe: 3.14159265 2.71828183 1 2 3 Statische Attribute und Methoden, bzw. Klassenmethoden, sind direkt einer Klasse zugeordnet und nicht einem Objekt. Der Unterschied zwischen statischen Methoden und Klassenmethoden ist, dass statische Methoden nicht auf Klassenattribute (bzw. statische Attribute) zugreifen können, Klassenmethoden aber schon. <code>pi</code> und <code>cnt</code> sind statische Attribute. <code>expo()</code> ist eine statische Methode. <code>counterPlus(cls)</code> ist eine Klassenmethode. Die Bezeichnung des Parameters als <code>cls</code> ist reine Konvention, man sollte sich aber daran halten. = 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. Die "arange"-Funktion aus der NumPy-Bibliothek wird später ein bisschen genauer behandelt (siehe [[Ing_Mathematik:_Python#Zahlenfolgen|Zahlenfolgen]]). <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. Die Farbe des Graphen kann man über den <code>color</code>-Schalter einstellen, z.B. auf rot setzen: plt.plot(x, y, color="red") Das ist nur eine Möglichkeit, es gibt auch andere, z.B. mittels * hexadezimaler Zahl, z.B. <code>color="#ff0000"</code> * Kurzform, z.B. <code>"r"</code> Statt <code>color</code> kann auch die Kurzform <code>c</code> verwendet werden. <u>Kleine Aufgabe:</u> Probieren Sie diese "Farbspielereien" am Computer aus. === Graphen mehrerer Funktionen und weiteres === ==== Mehrere Funktionen in einen Plot zeichnen ==== 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]] Einige weitere Bespiele zu <code>ls</code></code>: * - ... durchgezogene Linie * -. ... Strichpunkt-Linie * dashed ... gestrichelte Linie ==== Marker ==== Desweiteren kann man Marker verwenden. Beispiel: import matplotlib.pyplot as plt plt.plot(10, 10, "ob", markersize=30) plt.plot(20,20, ".r") plt.plot(30,30, "Dg", ms=15) plt.plot(40, 40, "ok", ms=20, mec="#FF5733", mew=3) plt.plot(50, 50, "vk", ms=20, mec="yellow", mew=3) plt.grid() plt.show() Das erste Zeichen im String nach den Koordinaten im plot-Befehl symbolisiert die Form (z.B. o für einen Kreis). Das zweite Zeichen steht für die Farbe (z.B. b für blue). <code>markersize</code> oder <code>ms</code> setzt die Größe des Markers. Ausgabe: [[Bild:PythonIng_formatpara1.svg|200px]] Einige weitere Marker: * * ... Stern * + ... Plus * h oder H ... Sechseck Man kann mit den Parametern * <code>markeredgecolor</code> oder kurz <code>mec</code> die Farbe des Markerrandes * <code>markeredgewidth</code> oder kurz <code>mew</code> die Dicke des Randes * <code>markerfacecolor</code> oder kurz <code>mfc</code> die Innenfarbe des Markers bestimmen. === 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]] === Anmerkungen hinzufügen === Anmerkungen (engl. annotations) lassen sich mit matplotlib einfach hinzufügen. Unter Anmerkung wird hier ein Pfeil mit Text verstanden. Beispiel: import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot([1,10], [1,10]) ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0)) plt.show() Ausgabe: [[Datei: PythonIng_ann1.svg|400px]] <code>shrink</code> verkürzt den Pfeil. Sinnvoll sind beim obigen Beispiel Werte irgendwo zwischen 0.0 und 0.3. <u>Kleine Aufgabe:</u> Probieren Sie verschiedene Pfeillängen aus. Die Schriftgröße verändern können Sie übrigens mit <code>fontsize</code> oder <code>size</code>, z.B. ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0), fontsize=25) Eine andere Schreibweise für Dictionaries, die wir weiter oben schon kennengelernt haben, die wir aber nochmals explizit aufschreiben wollen, ist ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops={"facecolor":"red", "shrink":0.0}) === Figure, Axes etc.=== [[Bild:PythonIng_figure33.svg|350px]] * fig (figure) ... das übergeordnete Fenster * ax (axes) ... "Plot", das eigentliche Zeichenfenster * axis ... die Koordinatenachsen * spine ... Begrenzungslinien des Zeichenfensters * tick ... Markierung an den Achsen des Zeichenfensters Beispiel (eine figure mit 6 axes, ax-Titel, y-axis-Titel): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=3, ncols=2) ax[0][0].text(0.1, 0.5, "11") ax[0][1].text(0.1, 0.5, "12") ax[1][0].text(0.1, 0.5, "21") ax[1][1].text(0.1, 0.5, "22") ax[2][0].text(0.1, 0.5, "31") ax[2][1].text(0.1, 0.5, "32") ax[0][0].set(ylabel="y-label", title="axes 1") plt.show() [[Bild:PythonIng_figure34.png|350px]] Beispiel (Achsenbereiche setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-1, 10, 1, 12)) plt.show() [[Bild:PythonIng_figure35.svg|350px]] Beispiel (Gitter einschalten): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-5, 10, 1, 12)) ax[0].minorticks_on() ax[0].grid(which='minor') ax[1].grid(which='major') plt.show() [[Bild:PythonIng_figure36.svg|350px]] Beispiel (ticks setzen) import matplotlib.pyplot as plt import matplotlib.ticker as tick fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") pos = [0, .2, .5] lab = ["a", "b", "c"] ax.xaxis.set_major_locator(tick.FixedLocator(pos)) ax.xaxis.set_major_formatter(tick.FixedFormatter(lab)) plt.show() [[Bild:PythonIng_figure37.svg|350px]] Beispiel (Achsenkreuz beliebig setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") ax.spines['top'].set_color('none') ax.spines['right'].set_color('none') ax.spines['bottom'].set_position(('data', 0.3)) ax.spines['left'].set_position(('data', 0.5)) plt.show() [[Bild:PythonIng_figure38.svg|350px]] Beispiel (Inset): import matplotlib.pyplot as plt # Hauptplot erstellen fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") # Inset hinzu: axin = ax.inset_axes([0.4, 0.4, 0.3, 0.3]) axin.text(0, 0, "Servus") axin.plot() plt.show() [[Bild:PythonIng_figure40.svg|350px]] Beispiel (Hintergrundfarben setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.set_facecolor("green") fig.set_facecolor("yellow") plt.plot([1, 3], [6, 8]) plt.show() [[Bild:PythonIng_figure39.svg|300px]] === GridSpec === Mit figures und axes lassen sich schon jede Menge an Grafikdetails einstellen. GridSpec ist dann der Turbo oder Raketenantrieb den matplotlib eingebaut hat. Naja, vielleicht ist das etwas übertrieben, aber GridSpec ist sozusagen ein flexibler Layout-Manager. Bisher haben wir immer gleich große axes gezeichnet. Mit GridSpec können wir Subplots über mehrere Zeilen und Spalten ziehen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) gs = fig.add_gridspec(3, 3) ax1 = fig.add_subplot(gs[0, :]) ax1.set_title("Bereich 1: gs[0, :]") ax2 = fig.add_subplot(gs[1:, 0]) ax2.set_title("Bereich 2: gs[1:, 0]") ax3 = fig.add_subplot(gs[1, 1:]) ax3.set_title("Bereich 3: gs[1, 1:]") ax4 = fig.add_subplot(gs[2, 1:]) ax4.set_title("Bereich 4: gs[2, 1:]") plt.show() [[Bild:PythonIng_gridspec1.svg|350px]] Erklärung zu <code>(gs[zeile, spalte])</code>: Slicing; damit legen Sie fest, welche Zellen ein Subplot belegt. Achtung! Die Indizes beginnen bei 0 und nicht bei 1 (d.h., die erste Zeile/Spalte hat den Index 0) * : ... alle * 1: ... 1 bis Ende * etc. <code>constrained_layout=True</code> sorgt dafür, dass sich die Subplots nicht überlappen. Aber auch die Spaltenbreiten und Zeilenhöhen lassen sich anpassen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) # Verhältnis erste zu zweite Zeile = 1.5:1 # Verhältnis erste zu zweite Spalte = 1:4 gs = fig.add_gridspec(2, 2, figure=fig, height_ratios=[1.5, 1], width_ratios=[1, 4]) ax1 = fig.add_subplot(gs[0, 0]) ax1.set_title("Bereich 1: gs[0, 0]") ax2 = fig.add_subplot(gs[0, 1]) ax2.set_title("Bereich 2: gs[0, 0]") ax3 = fig.add_subplot(gs[1, 0]) ax3.set_title("Bereich 3: gs[1, 0]") ax4 = fig.add_subplot(gs[1, 1:]) ax4.set_title("Bereich 4: gs[1, 1]") plt.show() [[Bild:PythonIng_gridspec2.svg|350px]] Auch die Ränder lassen sich einstellen. Beispiel 1: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.2, hspace=0.2) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec3.svg|300px]] * hspace ... height space, vertikaler Abstand zwischen den Zeilen * wspace ... width space, horizontaler Abstand zwischen den Spalten Und im Vergleich dazu Beispiel 2: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.3, right=0.7, bottom=0.3, top=0.7, wspace=0.3, hspace=0.3) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec4.svg|300px]] Es gibt noch einige weitere Details, die sich mit GridSpec variieren lassen (z.B. SubGridSpec ... verschachtelte Grid-Layouts). Aber wir belassen es vorerst dabei. Das bisher Gezeigte sollte für die meisten Probleme ausreichend sein. === 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|300px|center]] * 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|300px|center]] * 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. * Zeichnen Sie folgendes Bild (die Achsenbeschriftungen positionieren Sie z.B. mit den Befehlen <code>ax.set_xlabel("x", loc="right"); ax.set_ylabel("y", loc="top")</code>): [[Bild:PythonIng_aufgabe24.svg|400px|center]] * Zeichnen Sie mit Python, matplotlib und NumPy folgendes Bild: [[Bild:PythonIng_figure41.svg|400px|center]] * Zeichnen Sie mit GridSpec ein Layout, das in etwa wie folgendes aussieht (<code>height_ratios=[1, 2], width_ratios=[2, 1]</code>): [[Bild:PythonIng_aufgabe23.svg|300px|center]] == 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 und fortgeschrittenes 3D == === 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 build123d === Hiermit lassen sich einfach 3D-Modelle für CAD-Systeme erstellen. [https://build123d.readthedocs.io/en/latest/ build123d] ist ein externes Modul und muss vorab, wie schon für mehrere andere Module gezeigt, installiert werden (pip, venv etc.). Beispielcode: from build123d import * # Bauteil with BuildPart() as bauteil: # Zylinder erstellen Cylinder(100, 60) # Bohrung with BuildSketch(bauteil.faces().sort_by(Axis.Z)[-1]) as loch: Circle(radius=10) # negativ extrudieren extrude(amount=-60, mode=Mode.SUBTRACT) # Dem Bauteil z.B. die Farbe rot zuweisen # bauteil.part.color = Color("red", alpha=1.0) # Fertiges Modell exportieren export_step(bauteil.part, "bauteil.step") Ausgabe ({{W|Standard for the exchange of product model data | STEP}}-Datei, z.B. in {{W|FreeCAD}} importiert, Screenshot): [[Datei:PythonIng_build123d.jpg | 300px]] FreeCAD ist ein freies 3D-CAD-System. Die [[Ing_Mathematik:_Python#FreeCAD | Makroprogrammierung]] in diesem System wird später noch gezeigt. STEP steht für "'''St'''andard for the '''e'''xchange of '''p'''roduct model data" und ist ein Datenaustauschformat. === 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> == Sonstige Grafikbibliotheken == === Py5 === Im Buch ''Iacono et al.: Programmieren trainieren, mit über 150 Workouts in Java und Python; 3. Aufl., Hanser, 2023'' werden zahlreiche Aufgaben gestellt, die u.a. mit Processing zu lösen sind. Processing ist eine einfache Programmiersprache und Entwicklungsumgebung. Warum ich das erwähne? Weil in älteren Processing-Versionen auch die Programmiersprache Python 2 abgedeckt war. Bei neueren Versionen gibt es keine Python-Anbindung mehr (getestet nur mit openSUSE Tumbleweed und Python 3). Somit funktioniert der Python-Teil im genannten Buch zum Teil nicht mehr. Ist das tragisch, oder kann das Buch weggeschmissen werden? Nein! Es gibt Alternativen zu Processing. Zum einen die im Buch selbst genannten Bibliotheken tkinter oder pygame. Pygame wird dort auch kurz anhand eines Beispiels thematisiert. Aber noch besser ist es, die py5-Bibliothek zu verwenden (lt. Google KI vom 30. Sept. 2026: ''Der echte, moderne „spirituelle Nachfolger“''). Damit ist die Funktionalität von Processing gewährleistet. Man kann aber beliebige Editoren oder IDEs zur Programmerstellung verwenden, ohne an Processing als IDE gebunden zu sein. Nachfolgend werden ein paar Beispiele zu dieser externen Bibliothek gegeben. Der erste Schritt ist wieder, die py5-Bibliothek zu installieren (z.B. mittels pip in eine virtuelle Umgebung; Python>=3.10 und ein JDK>=17 müssen auch installiert sein (Stand Okt. 2026)). Dann kann es schon mit dem Programmieren losgehen. Beispiel (angelehnt an die ''Aufgabe W.2.3 Haus'' des genannten Buchs, jedoch etwas erweitert): import py5 def setup(): py5.size(600, 600) # Fenstergröße 600px x 600px py5.background(255, 255, 255) # Hintergrundfarbe ... weiss def draw(): # Haus zeichnen py5.fill(255, 255, 255) # Hintergrundfarbe ... weiss py5.stroke(255, 0, 0) # Linienfarbe ... rot py5.stroke_weight(3) # Linienbreite ... 3px py5.rect(150, 200, 300, 300) # Rechteck zeichnen (x, y, b, h) py5.triangle(150, 200, 300, 100, 450, 200) # Dreieck zeichnen py5.circle(250, 300, 70) # Kreis zeichnen (x, y, d) # Bemassung zeichnen py5.stroke(0, 255, 0) py5.stroke_weight(1) py5.line(300, 100, 550, 100) # Linie zeichnen py5.line(450, 200, 550, 200) py5.line(450, 500, 550, 500) py5.line(150, 500, 150, 550) py5.line(450, 500, 450, 550) py5.text_size(20) # Textgroesse py5.fill(0, 255, 0) # Textfarbe py5.text("300", 280, 550) # Text zeichnen py5.text("100", 500, 150) py5.text("300", 500, 350) # Gedrehtes Rechteck zeichnen py5.stroke(255, 255, 0) py5.stroke_weight(3) py5.fill(200, 200, 200) py5.translate(320, 260) py5.rotate(0.3) # Drehung (in Radiant) py5.rect(0, 0, 100, 50) # Startet den Sketch py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_1.png|300px]] Das skizzierte Haus ist mit einer rudimentären Bemaßung versehen und hat zwei Fenster, ein rundes und ein gedrehtes, trübes rechteckiges. Beispiel (Mausoperationen): import py5 def setup(): py5.size(600, 600) py5.background(255, 255, 255) def draw(): pass def mouse_pressed(): py5.text_size(15) py5.fill(0, 0, 255) x = py5.mouse_x y = py5.mouse_y py5.text("(%d, %d)" %(x, y), x, y) # Positionsdaten bei Maus-Klick schreiben py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_2.png|300px]] Obiges Bild zeigt das Resultat nach einigen Mausklicks. Man erkennt, dass links-oben der Koordinatennullpunkt ist. Die x-Achse zeigt nach rechts, die y-Achse nach unten. Beispiel (Fünfeck): import py5 import random def setup(): py5.size(400, 400) py5.no_loop() # statisches Bild (schont die Ressourcen) def draw(): py5.translate(py5.width / 2, py5.height / 2) # KS setzen radius = 150 eck_anzahl = 5 py5.fill(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # Füllfarbe auf Zufallswert setzen py5.stroke(155, 0, 0) py5.stroke_weight(5) # Fünfeck als Polygon zeichnen py5.begin_shape() for i in range(eck_anzahl): winkel = i * py5.TWO_PI / eck_anzahl - py5.HALF_PI x = py5.cos(winkel) * radius y = py5.sin(winkel) * radius py5.vertex(x, y) py5.end_shape(py5.CLOSE) py5.run_sketch() Ausgabe, bspw.: [[Datei:PythonIng_py5_5.png|200px]] <u>Kleine Aufgaben:</u> * Um die Funktionsweise von <code>py5.no_loop()</code> zu verstehen, kommentieren Sie die Anweisung im Beispiel einfach aus, und starten Sie das Programm erneut. * Zeichnen Sie ein Siebeneck. * Was passiert, wenn man <code>py5.CLOSE</code> im <code>py5.end_shape()</code>-Befehl weglässt? Beispiel (Grafik laden und zeichnen): import py5 image1 = None def setup(): global image1 py5.size(600, 600) py5.no_loop() image1 = py5.load_image("PythonIng_py5_1.png") def draw(): py5.image(image1, 0, 0) py5.run_sketch() Hier wird einfach das Bild vom Haus-Beispiel (Screenshot von weiter oben) geladen und gezeichnet. Beispiel (angelehnt an ''W.5.10 Wurfparabel'' des genannten Buches; mit OOP erstellt): import py5 class Wurfparabel(py5.Sketch): def __init__(self, v0, angle): super().__init__() self.v0 = v0 self.angle = self.radians(angle) self.g = 9.81 self.t = 0.0 self.scale = 1.5 def settings(self): self.size(800, 600) def setup(self): self.background(0, 0, 0) def draw(self): self.t += 0.1 self.translate(50, 500) # KS Zeichnen self.fill(255, 255, 0) self.stroke(255, 255, 0) self.stroke_weight(1) self.line(0, 0, 0, -300) self.line(0, 0, 700, 0) # Punkt zeichnen cx = self.v0 * self.cos(self.angle) * self.t * self.scale cy = -(self.v0 * self.sin(self.angle) * self.t - \ self.g/2 * self.t**2) * self.scale self.fill(255, 0, 0) self.stroke(255, 0, 0) self.circle(cx, cy, 3) # Stoppen bei Bodenkontakt if cy >= -5: self.no_loop() def wurfparabel(v0=50, angle=45): sketch = Wurfparabel(v0, angle) sketch.run_sketch() v0 = float(input("v0 = ")) winkel = float(input("winkel = ")) wurfparabel(v0=v0, angle=winkel) Eingabe auf der Konsole: v0 = 60 winkel = 45 Ausgabe: [[Datei:PythonIng_py5_13.png|200px]] Die benötigten Formeln der Bewegungsgleichung kann man leicht herleiten, bzw. man benutzt folgende Grafik als Hilfestellung: [[Datei:Wurfparabel_Zusammenfassung_aktualisierung.png|700px]] <u>Aufgabe:</u> Berechnen Sie den Scheitelpunkt und die Wurfweite bei gegebener Anfangsgeschwindigkeit und gegebenem Abwurfwinkel. Die Abwurfhöhe <math>y_0</math> sei 0. Der Wurf soll auf der Erde stattfinden (g = 9.81 m/s²). Die dazu benötigten Formeln können aus obiger Grafik entnommen werden (oder Sie leiten sie selbst her). Geben Sie die Ergebnisse auf der Konsole aus. Beispiel (eine einfache Animation): import py5 i = 0 def setup(): py5.size(600, 600) py5.frame_rate(5) py5.stroke_weight(20) def draw(): global i py5.background(255, 255, 255) py5.translate(py5.width/2, py5.height/2) py5.rotate(py5.PI/8 * i) py5.line(-100, 0, 100, 0) i += 1 py5.run_sketch() Es wird eine rotierende Linie gezeichnet (z.B. wie bei einem Installationsprozess verwendet - zeigt an, dass der Installationsfortschritt nicht eingefroren ist). Hierbei ist nur aufzupassen, dass der Hintergrund in <code>draw()</code> gesetzt wird, ansonsten würden die Linien additiv gezeichnet. Das ist zwar auch schön, aber eben nicht das gewünschte Resultat. Hiermit belassen wir es mit der py5-Bibliothek und wenden uns wieder "mathematischeren" Themen zu. = 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> standardmäßig aber sehr wohl. Jedoch kann man das bei <code>linspace</code> auch ändern. Dann ist der Stoppwert nicht mehr enthalten: linspace(start, stop, num, endpoint=False) == 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 === Beispiel: 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 Visualisierung: 0 1 2 +-----------+ 0 | 1 | 2 | 3 | +-----------+ 1 | 4 | 5 | '''6''' | +-----------+ Beispiel: import numpy as np A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] B = np.matrix(A) print(B[2::, ::2]) Ausgabe: [['20' '22'] ['30' '32'] ['40' '42']] Mit der NumPy-Bibliothek funktioniert das zweidimensionale Slicing. Mit normalen Listen/Tupels geht das nicht, was wir schon früher thematisiert haben. === 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]. Beispiel: import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: <Compressed Sparse Row sparse array of dtype 'float64' with 5 stored elements and shape (5, 5)> Coords Values (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 csr steht für '''c'''ompressed '''s'''parse '''r'''ow. Andere Möglichkeiten: * coo-Format ('''coo'''rdinate Format): <code>scipy.sparse.coo_array()</code> * lil-Format ('''li'''st of '''l'''ists): <code>scipy.sparse.lil_array()</code> * csc-Format ('''c'''ompressed '''s'''parse '''c'''olumn): <code>scipy.sparse.csc_array()</code> Es gibt auch <code>scipy.sparse.csr_matrix()</code> etc. Diese Befehle sind aber veraltet und es wird empfohlen, statt dessen die <code>..._array()</code>-Funktionen zu verwenden. <u>Aufgaben:</u> Recherchieren Sie im Internet * generell zum Thema "spärlich besetzte Matrizen mit Python". * Was sind die Vor-/Nachteile von csr-arrays gegenüber coo- und lil-arrays? * Wann verwendet man sinnvollerweise csr- und wann csc-arrays? * Wie wandelt man coo-arrays in csr-arrays um? = 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.] Siehe dazu auch das Kapitel [[Ing Mathematik: Numerisches Lösen von linearen Gleichungssystemen]]. Dort wird dieses Themengebiet u.a. mit weitergehenden Python-Skripten vertieft. == 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") <u>Aufgaben</u>: Ermitteln Sie mittels SymPy die <math>\mathcal{L}</math>-Transformierten und dann wieder die <math>\mathcal{L}^{-1}</math>-Transformationen (ähnlich wie beim obigen Beispiel): * <math>\sin(t) \circ\! -\! \bullet \ ?</math> * <math>t^3 \circ\! -\! \bullet \ ?</math> * <math>\text{e}^{\omega t} \circ\! -\! \bullet \ ?</math> =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 [https://mpmath.org/ 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). = pint - mit physikalischen Größen rechnen = [[File:Pint-Glas_UK.jpg|right|100px]] pint ist ein externes Python-Paket zur Manipulation von physikalischen Einheiten. Es muss vor der ersten Benutzung installiert werden (Vorgehensweise wie immer: pip, YaST, via venv etc.). Beispiel: import pint import numpy as np ureg = pint.UnitRegistry() # Einfache Berechnung print(3 * ureg.meter + 4 * ureg.cm) # Mit NumPy npeinzel = [3, 4] * ureg.meter + [2, 5] * ureg.cm print(npeinzel) npsum = np.sum(npeinzel) print(npsum) # Konvertierung von Einheiten npsum_inch = npsum.to(ureg.inch) print(npsum_inch) print(npeinzel.magnitude) print(npeinzel.units) print(npeinzel.dimensionality) Ausgabe: 3.04 meter [3.02 4.05] meter 7.07 meter 278.3464566929134 inch [3.02 4.05] meter [length] Die Dokumentation zu pint findet sich auf [https://pint.readthedocs.io/en/stable/]. =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. <small>Je größer die Anzahl der Datensätze n wird, umso weniger spielt das eine Rolle.</small> == 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. = Pandas kurz erklärt = Hier wird kurz auf das externe Modul Pandas eingegangen. Pandas steht für '''Pan'''el '''Da'''ta und ist ein Modul für die Datenanalyse und -manipulation. == Series == Vorstellbar ist eine "Series" wie eine einzelne Spalte in einem Tabellenkalkulationsprogramm. Beispiel (Index implizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) # Temperaturwerte print(temp) Ausgabe: 0 8.5 1 11.0 2 12.5 3 10.0 4 13.0 5 11.0 6 14.5 dtype: float64 Das Kürzel <code>pd</code> ist Konvention. Es seien Temperaturwerte gegeben. Die erste Spalte bei der Ausgabe ist der Index. Die zweite Spalte enthält die Werte. Beispiel (separater Zugriff auf den Index und auf die Werte): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) print(temp.index) print(temp.values) Ausgabe: RangeIndex(start=0, stop=7, step=1) [ 8.5 11. 12.5 10. 13. 11. 14.5] Beispiel (Index explizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) print("*** Die gesamte Series ***") print(temp) print("*** Temperatur am Donnerstag ***") print(temp["Do"]) print("*** Tage wärmer als 12 Grad ***") temp_gr_12 = temp[temp > 12] print(temp_gr_12) Ausgabe: *** Die gesamte Series *** Mo 8.5 Di 11.0 Mi 12.5 Do 10.0 Fr 13.0 Sa 11.0 So 14.5 dtype: float64 *** Temperatur am Donnerstag *** 10.0 *** Tage wärmer als 12 Grad *** Mi 12.5 Fr 13.0 So 14.5 dtype: float64 == DataFrame == Ein DataFrame entspricht einer Tabelle. Beispiel (Erzeugen eines DataFrames): import pandas as pd tag = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] v1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=tag) v2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=tag) v3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=tag) tab = pd.concat([v1, v2, v3], axis=1) tab.columns = stadt print(tab) print(type(tab)) Ausgabe: Linz Salzburg Innsbruck Mo 1 8 Hallo Di 2 9 16 Mi 3 10 17 Do 4 11 18 Fr 5 12 19 Sa 6 13 20 So 7 14 21 <class 'pandas.core.frame.DataFrame'> Wir haben hier drei Datenspalten (v1, v2, v3) mit den Spaltennamen "Linz", "Salzburg" und "Innsbruck", sowie sieben Zeilen "Mo" bis "So". Beispiel (Zugriff auf eine Spalte): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab["Salzburg"]) Ausgabe: Mo 8 Di 9 Mi 10 Do 11 Fr 12 Sa 13 So 14 Name: Salzburg, dtype: int64 Beispiel (Zugriff auf eine Zeile): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab.loc["Di"]) Ausgabe: Linz 2 Salzburg 9 Innsbruck 16 Name: Di, dtype: object == Grafiken erstellen == Hier sei nur ein Torten- oder Kreisdiagramm gezeichnet: import pandas as pd import matplotlib.pyplot as plt temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) expl = [0, 0.2, 0, 0, 0, 0, 0] temp.plot.pie(figsize=(5,5), explode=expl) plt.show() Ausgabe: [[Datei:IngPython_pandas1.svg|350px]] Die <code>figsize</code> hat nichts mit der Spaltenanzahl zu tun, sondern setzt die Fenster- oder Diagrammgröße in Inches (Breite * Höhe). Mit Pandas kann man auch noch andere Diagrammarten erstellen, z.B. Linien- oder Balkendiagramme. Dies wird hier aber nicht vorgeführt. == Arbeiten mit CSV- und LibreOffice-Dateien == Gegeben sei eine CSV-Datei test1.csv: Mai;Juni;Juli 12;17;45 15;65;65 16;32;23 4;14;7 23;17;Keine Daten verfügbar 67;Keine Daten verfügbar;45 15;24;12 Lesen von dieser CSV-Datei: import pandas as pd tab = pd.read_csv("test1.csv", sep=";") print(tab) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Schreiben in eine CSV-Datei test2.csv: import pandas as pd val = pd.read_csv("test1.csv", sep=";") val.to_csv("test2.csv", index=False) Ausgabedatei: Mai,Juni,Juli 12,17,45 15,65,65 16,32,23 4,14,7 23,17,Keine Daten verfügbar 67,Keine Daten verfügbar,45 15,24,12 Die Ausgabedatei ist beinahe gleich wie die Eingabedatei. Es werden aber Kommas statt Strichpunkten verwendet. Daher kommt auch der Name CSV ('''C'''omma '''S'''eparated '''V'''alue). Will man wieder Strichpunkte, so kann man dies über den Parameter <code>sep=";"</code> in der <code>to_csv</code>-Funktion machen. Um direkt von einer ODS-Datei (LibreOffice Calc, '''O'''pen '''D'''ocument '''S'''preadsheet) zu lesen muss das Modul odfpy extra installiert sein (per YaST, pip etc.). Gelesen wird dann folgendermaßen: import pandas as pd df = pd.read_excel('test1.ods', engine='odf') print(df) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Die Datei "test1.ods" sei die Datei, aus der das obige "test1.csv" generiert wurde. Schreiben kann man ebenfalls in eine ODS-Datei: import pandas as pd daten = { "Name": ["Hugo", "Richie", "Wilhelmine"], "Alter": [38, 30, 29], "Ort": ["Linz", "Innsbruck", "Salzburg"] } tab = pd.DataFrame(daten) with pd.ExcelWriter("test2.ods", engine="odf") as writer: tab.to_excel(writer, sheet_name="Daten", index=False) Es ergibt sich eine "test2.ods"-Datei (Screenshot): [[Datei:IngPython_pandas2.png|250px]] == Sonstiges == Hier seien stichpunktartig einige weitere Features von Pandas angeführt: * {{W|Pivot-Tabelle|Pivot-Tabellen}} (pivot ... Dreh-, Angelpunkt): Zweck - große Datenmengen schnell zusammenfassen, filtern und analysieren. * NaN ('''n'''ot '''a''' '''n'''umber): In einem Datensatz fehlt ein Wert oder ist nicht definiert. * groupby-Funktion: Daten strukturieren, aggregieren oder Zwischensummen bilden. * {{W|Binning#Datenverarbeitung_und_-analyse|Binning}}: Klassenbildung. * Mehrstufige Indizierung (Hierarchical Indexing oder MultiIndex) * Zeitreihen (Time Series) Für tiefergehende und weiterführende Informationen wird auf das Buch von ''Klein: Numerisches Python; Hanser, 2023'' und [https://pandas.pydata.org/ pandas] verwiesen. = 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) Das funktioniert übrigens nicht nur bei Funktionsannotationen, sondern auch mit Variablen etc. from typing import Final # "Konstante", ab Python 3.8 NUMBER: Final = 123 print(NUMBER) NUMBER = 125 print(NUMBER) # Variable a: int = 345 print(a) a = "Irgendwas" print(a) # Variable (Pipe-Symbol), ab Python 3.10 b : int|str = 567 print(b) b = "sonstiges" print(b) Ausgabe mit python: 123 125 345 Irgendwas 567 sonstiges Ausgabe mit mypy: test3.py:6: error: Cannot assign to final name "NUMBER" [misc] test3.py:12: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] Found 2 errors in 1 file (checked 1 source file) Python kennt übrigens keine Konstanten. Man kann diese mittels Großbuchstaben andeuten. Das ist aber nur Konvention, hat aber keine sonstigen Auswirkungen. Oder man verwendet wie oben gezeigt <code>Final</code>. Dann werfen Typchecker Errors aus. ==pydoc== pydoc ist ein Werkzeug, um Python-Module, -Funktionen und -Klassen zu dokumentieren. Beispiel <code>test1.py</code>: """Rechen-Modul. Funktionen die von pydoc gelesen werden. """ def multiply(a, b): """Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a * b def add(a, b): """Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a + b if __name__ == '__main__': print(multiply(3, 5)) print(add(4, 6)) Ausgabe mit <code>python3.13 -m pydoc test1</code> auf der Konsole: Help on module test1: NAME test1 - Rechen-Modul. DESCRIPTION Funktionen die von pydoc gelesen werden. FUNCTIONS add(a, b) Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl multiply(a, b) Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl FILE /home/xyz/tmp/test1.py Siehe zu diesem Thema z.B. auch [https://docs.python.org/3/library/pydoc.html]. Mittels <code>python3.13 -m pydoc -w test1</code> kann man auch eine HTML-Dokumentation erstellen. Wofür <code>if __name__ == '__main__':</code> steht, siehe z.B. etwas später im Abschnitt [[Ing_Mathematik:_Python#Doctests]]. == 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.3 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.5.1 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://build123d.readthedocs.io/en/latest/ build123d] * [https://docs.vtk.org/en/latest/api/python.html VTK] * [https://docs.bokeh.org/en/latest/docs/first_steps.html bokeh] * [https://py5coding.org/ py5] == 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] * [https://pint.readthedocs.io/en/stable/ pint] = 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] * Iacono et al.: Programmieren trainieren; 3. Aufl., Hanser, 2023, ISBN 978-3-446-47766-7 * 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}} 3bsuelt6qzrrpfziqgjq4dweat0v0n5 1092068 1092060 2026-10-11T10:42:39Z Intruder 1513 /* Strings und Platzhalter */ 1092068 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 (welche Verzeichnisse im PATH liegen, kann bspw. mit <code>echo $PATH</code> abgefragt werden). 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 == === Tests === 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) === Popularität === * An Beliebtheit rangiert Python mit Stand September 2026 mit einem Rating von 17,76% 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 Q1/2026 auf Rang 2, vor TypeScript und hinter JavaScript. * [https://pypl.github.io/PYPL.html PYPL] (Popularity of Programming Language Index) ist ein anderer Popularitätsindex für Programmiersprachen. Auch hier liegt Python mit großem Vorsprung auf dem ersten Platz (52,08%), gefolgt von Java (13,34%) und C/C++ (7,99%) (Stand September 2026). === Namensgebung === 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 in PyConUS24.jpg|{{W|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 <code>help</code> oder <code>help()</code> 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, bzw. der Eingabe von q, quit oder exit mit anschließendem Drücken der RETURN-Taste. == 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 Theoretisch kann man auch Umlaute, griechische Buchstaben u.ä. in Variablenbezeichnern nutzen, z.B. ößÜ = 16 α = 134 print(ößÜ) print(α) Ausgabe: 16 134 Davon wird aber aus offensichtlichen Gründen ({{W|Englisch als Lingua franca}}) abgeraten. Das Eurozeichen (€) etc. darf man ohnehin nicht in Variablenbezeichnern verwenden, dies ergäbe einen Syntaxfehler. Siehe dazu auch [https://peps.python.org/pep-3131/]. 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")) # new style string formatting print("Hallo {:s}" . format("Hugo")) # new style string formatting print("Hallo %s" % "Hugo") # %-String 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 f-Strings (ab Python 3.6) sind die modernere Variante gegenüber %-Strings oder das ''new style string formatting''. Sie sind in der Regel auch schneller. 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. Man beachte, dass {{W|Rundungsfehler}} auftreten können. D.h. man sollte bei der Verwendung von {{W|Gleitkommazahl|Gleitpunktzahlen}} niemals auf absolute Genauigkeit prüfen, sondern eine Toleranz (einen <math>\varepsilon</math>-Wert) vorgeben. Ein klassisches Beispiel, das ein falsches Ergebnis liefert, ist: i = 0.1 + 0.2 print(i) if i == 0.3: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 NOK Und das, obwohl man schon in der Volksschule lernt, dass 0.1 + 0.2 gleich 0.3 ist. Korrekt muss das obige Beispiel z.B. so umgeschrieben werden: ist = 0.1 + 0.2 soll = 0.3 epsilon = 0.000001 print(ist) if abs(ist-soll) < epsilon: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 OK Alternativ kann auch die <code>math.isclose()</code>-Funktion verwendet werden. Das sei aber hier nur erwähnt und nicht konkret ausgeführt. <u>Aufgabe:</u> Recherchieren Sie im Internet zum Thema <code>math.isclose()</code>-Funktion. === 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 == Kopfgesteuerte Schleife, Endlosschleife und fußgesteuerte Schleife: [[File:Programmingloops.svg|450px]] === 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 Der <code>break</code>-Befehl wird etwas später noch einmal kurz thematisiert. === 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!") print("Setze Programm fort!") Ausgabe: Das Ergebnis ist unendlich Setze Programm fort! 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). Danach wird das Programm fortgesetzt. 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. Neu in Python 3.14 ist, dass mehrere Ausnahmen auch ohne Klammern angegeben werden können, z.B.: bis inkl. Python 3.13 nur: except (ZeroDivisionError, ValueError): ab Python 3.14 auch: except ZeroDivisionError, ValueError: == 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 c, d = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(c) print(d) Ausgabe: 8 15 Es werden r1 und r2 zurückgegeben und den Variablen c und d zugewiesen. (r1, r2) und (c, d) sind Tupel. Was Tupel konkret sind, siehe etwas später in diesem Kapitel. 3 und 5 sind die aktuellen Parameter (die Argumente). a und b sind die formalen Parameter. Die Zuweisung der aktuellen zu den formalen Parametern erfolgt in der gegebenen Reihenfolge, also die Zahl 3 wird an a übergeben und die Zahl 5 an b. Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 c, d = mathFunc(3, 5) print(c) print(d) e, f = mathFunc(5) print(e) print(f) g, h = mathFunc(b=6) print(g) print(h) Ausgabe: 8 15 25 100 16 60 Erklärung: * Erster <code>mathFunc</code>-Aufruf: Es werden, wie weiter oben, die Argumente 3 und 5 an die formalen Parameter a und b übergeben. Die Rückgabewerte r1 und r2 werden an c und d übergeben. Somit ist c=8 und d=15. * Zweiter <code>mathFunc</code>-Aufruf: Es wird nur das Argment 5 an a übergeben. b behält den Vorgabewert 20. D.h. e= 25 und f=100. * Dritter <code>mathFunc</code>-Aufruf: Es wird nur die Zahl 6 an b übergeben, a behält seinen Vorgabewert 10. Somit ergibt sich g=16 und h=60. Beispiel (Schlüsselwortargumente vs. Positionsargumente): def mathFunc(a, b): r1 = a - b r2 = a / b return r1, r2 c, d = mathFunc(b=3, a=5) # Schlüsselwortargumente e, f = mathFunc(3, 5) # Positionsargumente print(c) print(d) print(e) print(f) Ausgabe: 2 1.6666666666666667 -2 0.6 Durch die Verwendung von Schlüsselwortargumenten können die Argumente in beliebiger Reihenfolge angegeben werden. Bei den Positionsargumenten ist die Reihenfolge vorgegeben. Daraus resultiert auch der Unterschied bei den obigen Ausgaben. Werden beide Arten in einem Aufruf gemischt, so müssen die Positionsargumente zuerst angegeben werden. Den Unterschied zwischen beiden Argumentarten zu kennen ist wichtig, nicht nur bei selbst erstellten Funktionen. Auch beim Aufruf von Python-Funktionen verfolgt einen die Mischung von Schlüsselwort- und Positionsargumenten auf Schritt und Tritt, z.B. beim etwas später folgenden Abschnitt über "Grafiken zeichnen": plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") Bei dieser <code>plot</code>-Funktion aus dem Modul <code>matplotlib.pyplot</code> sind <code>x</code> und <code>y1</code> Positionsargumente. <code>label, lw</code> und <code>ls</code> sind die Namen der Schlüsselwortargumente. Die Schlüsselwortargumente könnten auch in anderer Reihenfolge angeschrieben werden, nicht jedoch die Positionsargumente! Siehe zu <code>matplotlib.pyplot.plot</code> auch die Seite [https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html]. == 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 und Listen == 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. 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. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. # 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 (eine Liste von Wörtern zu einem einzigen zusammenhängenden Text verbinden): woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt == Dictionaries und Mengen == 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'> Leere Mengen lassen sich mit <code>set()</code> erzeugen: menge = set() # leere Menge print(menge) menge.add("Hallo") # Element hinzufügen print(menge) Ausgabe: set() {'Hallo'} Beispiel (einige Mengenoperationen): mengeA = {"Hallo", "Welt"} mengeB = {1, 2, "Welt", 3} # Vereinigung print(mengeA | mengeB) print(mengeA.union(mengeB)) # Durchschnitt print(mengeA & mengeB) print(mengeA.intersection(mengeB)) # Differenz print(mengeA - mengeB) print(mengeA.difference(mengeB)) # Symmetrische Differenz print(mengeA ^ mengeB) print(mengeA.symmetric_difference(mengeB)) # Teilmenge print(mengeA.issubset(mengeB)) # Mehrere Elemente hinzu mengeA.update(["Super", 67, 125]) print(mengeA) # Element löschen mengeA.discard("Hallo") print(mengeA) Ausgabe: {1, 2, 3, 'Welt', 'Hallo'} {1, 2, 3, 'Welt', 'Hallo'} {'Welt'} {'Welt'} {'Hallo'} {'Hallo'} {1, 2, 3, 'Hallo'} {1, 2, 3, 'Hallo'} False {67, 'Super', 'Welt', 'Hallo', 125} {67, 'Super', 'Welt', 125} Es gibt schon seit "ewigen" Zeiten unveränderbare Mengen, die mittels <code>frozenset</code> eingeleitet werden. Neu ab Python 3.15 ist dagegen das Pendant <code>frozendict</code>, das unveränderbare Dictionaries darstellt. fdict = frozendict({"vorname":"Hugo", "nachname":"Meister"}) fset = frozenset({"A", 1, "A"}) print(fdict) print(fdict["vorname"]) print (fset) try: fdict["vorname"] = "Max" print(fdict["vorname"]) except TypeError as e: print(f"Fehler: {e}") try: fset.add("B") print(fset) except AttributeError as e: print(f"Fehler: {e}") Ausgabe: frozendict({'vorname': 'Hugo', 'nachname': 'Meister'}) Hugo frozenset({1, 'A'}) Fehler: 'frozendict' object does not support item assignment Fehler: 'frozenset' object has no attribute 'add' == 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: x = ("a", "b", "c", "d", "e") print(x[2]) print(x[-3]) print(x) print(x[:]) print(x[2:]) print(x[:2]) print(x[::-1]) Ausgabe: c c ('a', 'b', 'c', 'd', 'e') ('a', 'b', 'c', 'd', 'e') ('c', 'd', 'e') ('a', 'b') ('e', 'd', 'c', 'b', 'a') Visualisierung: '''a''' '''b''' '''c''' '''d''' '''e''' +-----------------------------+ | 0 | 1 |//'''2'''//| 3 | 4 | +-----------------------------+ | -5 | -4 |/'''-3'''//| -2 | -1 | +-----------------------------+ |/////////////////////////////| +-----------------------------+ |/////////////''':'''///////////////| +-----------------------------+ | | |///////'''2:'''////////| +-----------------------------+ |////''':2'''/////| | | | +-----------------------------+ |\\\\\<--\\\\\'''::-1'''\\\\<--\\\\\| +-----------------------------+ Beispiel: x = ("a", "b", "c", "d", "e") print(x[2::2]) print(x[2::-1]) print(x[2:0:-1]) Ausgabe: ('c', 'e') ('c', 'b', 'a') ('c', 'b') Hier sieht man sehr schön, dass beim letzen <code>print</code>-Befehl die 0, die in diesem Fall den Buchstaben "a" repräsentiert, als letzer Wert nicht mehr dazugehört (wie weiter oben angedeutet ist der End- oder Stoppwert exklusiv). Will man den Buchstaben "a" dazunehmen, so ist die vorletzte Zeile die richtige. Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH Mit zweidimensionalen Listen/Tupels wird das Ganze schon bedeutend komplexer. Beispiel: Verkettetes Slicing A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] print(A[2::][::2]) Ausgabe: [['20', '21', '22', '23'], ['40', '41', '42', '43']] Das erste Slicing erstellt folgenden Slice: [["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] Das zweite Slicing wird dann auf diesen Slice angewendet und ergibt das genannte Endergebnis. Will man Spalten statt Zeilen, so funktioniert dieser direkte Weg nicht. Man verwendet dann List Comprehensions oder die NumPy-Bibliothek. Diese Bibliothek erlaubt zweidimensionales Slicing. Sie wird erst später bei den Grafiken eingeführt. Deshalb wird hier dazu noch kein Beispiel gezeigt. = 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 (ist der sogenannte Konstruktor). 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 strikt/stark private ist. Wird nur ein Unterstrich verwendet, so deutet dies auf schwach/weakly private hin. Letzteres wird in Python deutlich häufiger verwendet. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. Besonderheiten bei stark und schwach private: * '''stark private''': ** zwei führende Unterstriche ** Name Mangling: Name wird zu <code>_Klassenname__variablenname</code> ** Schutz vor Namenskollisionen bei Vererbung ** am Ende des Namens dürfen keine zwei Unterstriche verwendet werden. Das wären dann Magic Methods oder Dunder-Methods (z.B. __init__). Diese sind public. * '''schwach private''': ** ein führender Unterstrich ** Name bleibt gleich ** ist nur ein Hinweis an die Programmierer, kein starker Schutz ** <code>from modul import *</code>: Objekte mit einem führenden Unterstrich werden standardmäßig nicht importiert ** Standard für die Kapselung bei Python. Schätzung: Weit mehr als 80% der nicht-öffentlichen Attribute/Methoden werden als schwach private deklariert. Weniger als 5-10% als stark private (Quelle: Google KI) == 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 == === Einfachvererbung === [[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 === Mehrfachvererbung === Mit Python ist auch Mehrfachvererbung möglich, z.B.: class A: def _printA(self): print("Klasse A") class B: def _printB(self): print("Klasse B") class C(A,B): def __init__(self): self._printA() self._printB() c = C() Ausgabe: Klasse A Klasse B Die Klasse C erbt von A und B. In UML sieht das in etwa so aus: +-----------------------------+ +-------------------------+ | A | | B | +-----------------------------+ +-------------------------+ | | | | +-----------------------------+ +-------------------------+ | # printA() | | # printB() | +-----------------------------+ +-------------------------+ . . / \ / \ --- --- | | | | +---------------------+ | C | +---------------------+ | | +---------------------+ | | +---------------------+ <code>printA()</code> und <code>printB()</code> sind schwach private (in UML als <code>protected</code> mit einem # dargestellt). Es ist aber zu beachten, dass schwach private nur Konvention ist. Praktisch könnte man auch von außerhalb auf diese Methoden zugreifen. Das soll man aber nicht tun, und das wird in Python durch einen Unterstrich angedeutet. == Statische Attribute, statische Methoden und Klassenmethoden == Beispiel: class Math: pi = 3.14159265 cnt = 0 @staticmethod def expo(): return 2.71828183 @classmethod def counterPlus(cls): cls.cnt += 1 print(Math.pi) print(Math.expo()) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Ausgabe: 3.14159265 2.71828183 1 2 3 Statische Attribute und Methoden, bzw. Klassenmethoden, sind direkt einer Klasse zugeordnet und nicht einem Objekt. Der Unterschied zwischen statischen Methoden und Klassenmethoden ist, dass statische Methoden nicht auf Klassenattribute (bzw. statische Attribute) zugreifen können, Klassenmethoden aber schon. <code>pi</code> und <code>cnt</code> sind statische Attribute. <code>expo()</code> ist eine statische Methode. <code>counterPlus(cls)</code> ist eine Klassenmethode. Die Bezeichnung des Parameters als <code>cls</code> ist reine Konvention, man sollte sich aber daran halten. = 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. Die "arange"-Funktion aus der NumPy-Bibliothek wird später ein bisschen genauer behandelt (siehe [[Ing_Mathematik:_Python#Zahlenfolgen|Zahlenfolgen]]). <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. Die Farbe des Graphen kann man über den <code>color</code>-Schalter einstellen, z.B. auf rot setzen: plt.plot(x, y, color="red") Das ist nur eine Möglichkeit, es gibt auch andere, z.B. mittels * hexadezimaler Zahl, z.B. <code>color="#ff0000"</code> * Kurzform, z.B. <code>"r"</code> Statt <code>color</code> kann auch die Kurzform <code>c</code> verwendet werden. <u>Kleine Aufgabe:</u> Probieren Sie diese "Farbspielereien" am Computer aus. === Graphen mehrerer Funktionen und weiteres === ==== Mehrere Funktionen in einen Plot zeichnen ==== 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]] Einige weitere Bespiele zu <code>ls</code></code>: * - ... durchgezogene Linie * -. ... Strichpunkt-Linie * dashed ... gestrichelte Linie ==== Marker ==== Desweiteren kann man Marker verwenden. Beispiel: import matplotlib.pyplot as plt plt.plot(10, 10, "ob", markersize=30) plt.plot(20,20, ".r") plt.plot(30,30, "Dg", ms=15) plt.plot(40, 40, "ok", ms=20, mec="#FF5733", mew=3) plt.plot(50, 50, "vk", ms=20, mec="yellow", mew=3) plt.grid() plt.show() Das erste Zeichen im String nach den Koordinaten im plot-Befehl symbolisiert die Form (z.B. o für einen Kreis). Das zweite Zeichen steht für die Farbe (z.B. b für blue). <code>markersize</code> oder <code>ms</code> setzt die Größe des Markers. Ausgabe: [[Bild:PythonIng_formatpara1.svg|200px]] Einige weitere Marker: * * ... Stern * + ... Plus * h oder H ... Sechseck Man kann mit den Parametern * <code>markeredgecolor</code> oder kurz <code>mec</code> die Farbe des Markerrandes * <code>markeredgewidth</code> oder kurz <code>mew</code> die Dicke des Randes * <code>markerfacecolor</code> oder kurz <code>mfc</code> die Innenfarbe des Markers bestimmen. === 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]] === Anmerkungen hinzufügen === Anmerkungen (engl. annotations) lassen sich mit matplotlib einfach hinzufügen. Unter Anmerkung wird hier ein Pfeil mit Text verstanden. Beispiel: import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot([1,10], [1,10]) ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0)) plt.show() Ausgabe: [[Datei: PythonIng_ann1.svg|400px]] <code>shrink</code> verkürzt den Pfeil. Sinnvoll sind beim obigen Beispiel Werte irgendwo zwischen 0.0 und 0.3. <u>Kleine Aufgabe:</u> Probieren Sie verschiedene Pfeillängen aus. Die Schriftgröße verändern können Sie übrigens mit <code>fontsize</code> oder <code>size</code>, z.B. ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0), fontsize=25) Eine andere Schreibweise für Dictionaries, die wir weiter oben schon kennengelernt haben, die wir aber nochmals explizit aufschreiben wollen, ist ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops={"facecolor":"red", "shrink":0.0}) === Figure, Axes etc.=== [[Bild:PythonIng_figure33.svg|350px]] * fig (figure) ... das übergeordnete Fenster * ax (axes) ... "Plot", das eigentliche Zeichenfenster * axis ... die Koordinatenachsen * spine ... Begrenzungslinien des Zeichenfensters * tick ... Markierung an den Achsen des Zeichenfensters Beispiel (eine figure mit 6 axes, ax-Titel, y-axis-Titel): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=3, ncols=2) ax[0][0].text(0.1, 0.5, "11") ax[0][1].text(0.1, 0.5, "12") ax[1][0].text(0.1, 0.5, "21") ax[1][1].text(0.1, 0.5, "22") ax[2][0].text(0.1, 0.5, "31") ax[2][1].text(0.1, 0.5, "32") ax[0][0].set(ylabel="y-label", title="axes 1") plt.show() [[Bild:PythonIng_figure34.png|350px]] Beispiel (Achsenbereiche setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-1, 10, 1, 12)) plt.show() [[Bild:PythonIng_figure35.svg|350px]] Beispiel (Gitter einschalten): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-5, 10, 1, 12)) ax[0].minorticks_on() ax[0].grid(which='minor') ax[1].grid(which='major') plt.show() [[Bild:PythonIng_figure36.svg|350px]] Beispiel (ticks setzen) import matplotlib.pyplot as plt import matplotlib.ticker as tick fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") pos = [0, .2, .5] lab = ["a", "b", "c"] ax.xaxis.set_major_locator(tick.FixedLocator(pos)) ax.xaxis.set_major_formatter(tick.FixedFormatter(lab)) plt.show() [[Bild:PythonIng_figure37.svg|350px]] Beispiel (Achsenkreuz beliebig setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") ax.spines['top'].set_color('none') ax.spines['right'].set_color('none') ax.spines['bottom'].set_position(('data', 0.3)) ax.spines['left'].set_position(('data', 0.5)) plt.show() [[Bild:PythonIng_figure38.svg|350px]] Beispiel (Inset): import matplotlib.pyplot as plt # Hauptplot erstellen fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") # Inset hinzu: axin = ax.inset_axes([0.4, 0.4, 0.3, 0.3]) axin.text(0, 0, "Servus") axin.plot() plt.show() [[Bild:PythonIng_figure40.svg|350px]] Beispiel (Hintergrundfarben setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.set_facecolor("green") fig.set_facecolor("yellow") plt.plot([1, 3], [6, 8]) plt.show() [[Bild:PythonIng_figure39.svg|300px]] === GridSpec === Mit figures und axes lassen sich schon jede Menge an Grafikdetails einstellen. GridSpec ist dann der Turbo oder Raketenantrieb den matplotlib eingebaut hat. Naja, vielleicht ist das etwas übertrieben, aber GridSpec ist sozusagen ein flexibler Layout-Manager. Bisher haben wir immer gleich große axes gezeichnet. Mit GridSpec können wir Subplots über mehrere Zeilen und Spalten ziehen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) gs = fig.add_gridspec(3, 3) ax1 = fig.add_subplot(gs[0, :]) ax1.set_title("Bereich 1: gs[0, :]") ax2 = fig.add_subplot(gs[1:, 0]) ax2.set_title("Bereich 2: gs[1:, 0]") ax3 = fig.add_subplot(gs[1, 1:]) ax3.set_title("Bereich 3: gs[1, 1:]") ax4 = fig.add_subplot(gs[2, 1:]) ax4.set_title("Bereich 4: gs[2, 1:]") plt.show() [[Bild:PythonIng_gridspec1.svg|350px]] Erklärung zu <code>(gs[zeile, spalte])</code>: Slicing; damit legen Sie fest, welche Zellen ein Subplot belegt. Achtung! Die Indizes beginnen bei 0 und nicht bei 1 (d.h., die erste Zeile/Spalte hat den Index 0) * : ... alle * 1: ... 1 bis Ende * etc. <code>constrained_layout=True</code> sorgt dafür, dass sich die Subplots nicht überlappen. Aber auch die Spaltenbreiten und Zeilenhöhen lassen sich anpassen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) # Verhältnis erste zu zweite Zeile = 1.5:1 # Verhältnis erste zu zweite Spalte = 1:4 gs = fig.add_gridspec(2, 2, figure=fig, height_ratios=[1.5, 1], width_ratios=[1, 4]) ax1 = fig.add_subplot(gs[0, 0]) ax1.set_title("Bereich 1: gs[0, 0]") ax2 = fig.add_subplot(gs[0, 1]) ax2.set_title("Bereich 2: gs[0, 0]") ax3 = fig.add_subplot(gs[1, 0]) ax3.set_title("Bereich 3: gs[1, 0]") ax4 = fig.add_subplot(gs[1, 1:]) ax4.set_title("Bereich 4: gs[1, 1]") plt.show() [[Bild:PythonIng_gridspec2.svg|350px]] Auch die Ränder lassen sich einstellen. Beispiel 1: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.2, hspace=0.2) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec3.svg|300px]] * hspace ... height space, vertikaler Abstand zwischen den Zeilen * wspace ... width space, horizontaler Abstand zwischen den Spalten Und im Vergleich dazu Beispiel 2: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.3, right=0.7, bottom=0.3, top=0.7, wspace=0.3, hspace=0.3) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec4.svg|300px]] Es gibt noch einige weitere Details, die sich mit GridSpec variieren lassen (z.B. SubGridSpec ... verschachtelte Grid-Layouts). Aber wir belassen es vorerst dabei. Das bisher Gezeigte sollte für die meisten Probleme ausreichend sein. === 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|300px|center]] * 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|300px|center]] * 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. * Zeichnen Sie folgendes Bild (die Achsenbeschriftungen positionieren Sie z.B. mit den Befehlen <code>ax.set_xlabel("x", loc="right"); ax.set_ylabel("y", loc="top")</code>): [[Bild:PythonIng_aufgabe24.svg|400px|center]] * Zeichnen Sie mit Python, matplotlib und NumPy folgendes Bild: [[Bild:PythonIng_figure41.svg|400px|center]] * Zeichnen Sie mit GridSpec ein Layout, das in etwa wie folgendes aussieht (<code>height_ratios=[1, 2], width_ratios=[2, 1]</code>): [[Bild:PythonIng_aufgabe23.svg|300px|center]] == 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 und fortgeschrittenes 3D == === 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 build123d === Hiermit lassen sich einfach 3D-Modelle für CAD-Systeme erstellen. [https://build123d.readthedocs.io/en/latest/ build123d] ist ein externes Modul und muss vorab, wie schon für mehrere andere Module gezeigt, installiert werden (pip, venv etc.). Beispielcode: from build123d import * # Bauteil with BuildPart() as bauteil: # Zylinder erstellen Cylinder(100, 60) # Bohrung with BuildSketch(bauteil.faces().sort_by(Axis.Z)[-1]) as loch: Circle(radius=10) # negativ extrudieren extrude(amount=-60, mode=Mode.SUBTRACT) # Dem Bauteil z.B. die Farbe rot zuweisen # bauteil.part.color = Color("red", alpha=1.0) # Fertiges Modell exportieren export_step(bauteil.part, "bauteil.step") Ausgabe ({{W|Standard for the exchange of product model data | STEP}}-Datei, z.B. in {{W|FreeCAD}} importiert, Screenshot): [[Datei:PythonIng_build123d.jpg | 300px]] FreeCAD ist ein freies 3D-CAD-System. Die [[Ing_Mathematik:_Python#FreeCAD | Makroprogrammierung]] in diesem System wird später noch gezeigt. STEP steht für "'''St'''andard for the '''e'''xchange of '''p'''roduct model data" und ist ein Datenaustauschformat. === 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> == Sonstige Grafikbibliotheken == === Py5 === Im Buch ''Iacono et al.: Programmieren trainieren, mit über 150 Workouts in Java und Python; 3. Aufl., Hanser, 2023'' werden zahlreiche Aufgaben gestellt, die u.a. mit Processing zu lösen sind. Processing ist eine einfache Programmiersprache und Entwicklungsumgebung. Warum ich das erwähne? Weil in älteren Processing-Versionen auch die Programmiersprache Python 2 abgedeckt war. Bei neueren Versionen gibt es keine Python-Anbindung mehr (getestet nur mit openSUSE Tumbleweed und Python 3). Somit funktioniert der Python-Teil im genannten Buch zum Teil nicht mehr. Ist das tragisch, oder kann das Buch weggeschmissen werden? Nein! Es gibt Alternativen zu Processing. Zum einen die im Buch selbst genannten Bibliotheken tkinter oder pygame. Pygame wird dort auch kurz anhand eines Beispiels thematisiert. Aber noch besser ist es, die py5-Bibliothek zu verwenden (lt. Google KI vom 30. Sept. 2026: ''Der echte, moderne „spirituelle Nachfolger“''). Damit ist die Funktionalität von Processing gewährleistet. Man kann aber beliebige Editoren oder IDEs zur Programmerstellung verwenden, ohne an Processing als IDE gebunden zu sein. Nachfolgend werden ein paar Beispiele zu dieser externen Bibliothek gegeben. Der erste Schritt ist wieder, die py5-Bibliothek zu installieren (z.B. mittels pip in eine virtuelle Umgebung; Python>=3.10 und ein JDK>=17 müssen auch installiert sein (Stand Okt. 2026)). Dann kann es schon mit dem Programmieren losgehen. Beispiel (angelehnt an die ''Aufgabe W.2.3 Haus'' des genannten Buchs, jedoch etwas erweitert): import py5 def setup(): py5.size(600, 600) # Fenstergröße 600px x 600px py5.background(255, 255, 255) # Hintergrundfarbe ... weiss def draw(): # Haus zeichnen py5.fill(255, 255, 255) # Hintergrundfarbe ... weiss py5.stroke(255, 0, 0) # Linienfarbe ... rot py5.stroke_weight(3) # Linienbreite ... 3px py5.rect(150, 200, 300, 300) # Rechteck zeichnen (x, y, b, h) py5.triangle(150, 200, 300, 100, 450, 200) # Dreieck zeichnen py5.circle(250, 300, 70) # Kreis zeichnen (x, y, d) # Bemassung zeichnen py5.stroke(0, 255, 0) py5.stroke_weight(1) py5.line(300, 100, 550, 100) # Linie zeichnen py5.line(450, 200, 550, 200) py5.line(450, 500, 550, 500) py5.line(150, 500, 150, 550) py5.line(450, 500, 450, 550) py5.text_size(20) # Textgroesse py5.fill(0, 255, 0) # Textfarbe py5.text("300", 280, 550) # Text zeichnen py5.text("100", 500, 150) py5.text("300", 500, 350) # Gedrehtes Rechteck zeichnen py5.stroke(255, 255, 0) py5.stroke_weight(3) py5.fill(200, 200, 200) py5.translate(320, 260) py5.rotate(0.3) # Drehung (in Radiant) py5.rect(0, 0, 100, 50) # Startet den Sketch py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_1.png|300px]] Das skizzierte Haus ist mit einer rudimentären Bemaßung versehen und hat zwei Fenster, ein rundes und ein gedrehtes, trübes rechteckiges. Beispiel (Mausoperationen): import py5 def setup(): py5.size(600, 600) py5.background(255, 255, 255) def draw(): pass def mouse_pressed(): py5.text_size(15) py5.fill(0, 0, 255) x = py5.mouse_x y = py5.mouse_y py5.text("(%d, %d)" %(x, y), x, y) # Positionsdaten bei Maus-Klick schreiben py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_2.png|300px]] Obiges Bild zeigt das Resultat nach einigen Mausklicks. Man erkennt, dass links-oben der Koordinatennullpunkt ist. Die x-Achse zeigt nach rechts, die y-Achse nach unten. Beispiel (Fünfeck): import py5 import random def setup(): py5.size(400, 400) py5.no_loop() # statisches Bild (schont die Ressourcen) def draw(): py5.translate(py5.width / 2, py5.height / 2) # KS setzen radius = 150 eck_anzahl = 5 py5.fill(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # Füllfarbe auf Zufallswert setzen py5.stroke(155, 0, 0) py5.stroke_weight(5) # Fünfeck als Polygon zeichnen py5.begin_shape() for i in range(eck_anzahl): winkel = i * py5.TWO_PI / eck_anzahl - py5.HALF_PI x = py5.cos(winkel) * radius y = py5.sin(winkel) * radius py5.vertex(x, y) py5.end_shape(py5.CLOSE) py5.run_sketch() Ausgabe, bspw.: [[Datei:PythonIng_py5_5.png|200px]] <u>Kleine Aufgaben:</u> * Um die Funktionsweise von <code>py5.no_loop()</code> zu verstehen, kommentieren Sie die Anweisung im Beispiel einfach aus, und starten Sie das Programm erneut. * Zeichnen Sie ein Siebeneck. * Was passiert, wenn man <code>py5.CLOSE</code> im <code>py5.end_shape()</code>-Befehl weglässt? Beispiel (Grafik laden und zeichnen): import py5 image1 = None def setup(): global image1 py5.size(600, 600) py5.no_loop() image1 = py5.load_image("PythonIng_py5_1.png") def draw(): py5.image(image1, 0, 0) py5.run_sketch() Hier wird einfach das Bild vom Haus-Beispiel (Screenshot von weiter oben) geladen und gezeichnet. Beispiel (angelehnt an ''W.5.10 Wurfparabel'' des genannten Buches; mit OOP erstellt): import py5 class Wurfparabel(py5.Sketch): def __init__(self, v0, angle): super().__init__() self.v0 = v0 self.angle = self.radians(angle) self.g = 9.81 self.t = 0.0 self.scale = 1.5 def settings(self): self.size(800, 600) def setup(self): self.background(0, 0, 0) def draw(self): self.t += 0.1 self.translate(50, 500) # KS Zeichnen self.fill(255, 255, 0) self.stroke(255, 255, 0) self.stroke_weight(1) self.line(0, 0, 0, -300) self.line(0, 0, 700, 0) # Punkt zeichnen cx = self.v0 * self.cos(self.angle) * self.t * self.scale cy = -(self.v0 * self.sin(self.angle) * self.t - \ self.g/2 * self.t**2) * self.scale self.fill(255, 0, 0) self.stroke(255, 0, 0) self.circle(cx, cy, 3) # Stoppen bei Bodenkontakt if cy >= -5: self.no_loop() def wurfparabel(v0=50, angle=45): sketch = Wurfparabel(v0, angle) sketch.run_sketch() v0 = float(input("v0 = ")) winkel = float(input("winkel = ")) wurfparabel(v0=v0, angle=winkel) Eingabe auf der Konsole: v0 = 60 winkel = 45 Ausgabe: [[Datei:PythonIng_py5_13.png|200px]] Die benötigten Formeln der Bewegungsgleichung kann man leicht herleiten, bzw. man benutzt folgende Grafik als Hilfestellung: [[Datei:Wurfparabel_Zusammenfassung_aktualisierung.png|700px]] <u>Aufgabe:</u> Berechnen Sie den Scheitelpunkt und die Wurfweite bei gegebener Anfangsgeschwindigkeit und gegebenem Abwurfwinkel. Die Abwurfhöhe <math>y_0</math> sei 0. Der Wurf soll auf der Erde stattfinden (g = 9.81 m/s²). Die dazu benötigten Formeln können aus obiger Grafik entnommen werden (oder Sie leiten sie selbst her). Geben Sie die Ergebnisse auf der Konsole aus. Beispiel (eine einfache Animation): import py5 i = 0 def setup(): py5.size(600, 600) py5.frame_rate(5) py5.stroke_weight(20) def draw(): global i py5.background(255, 255, 255) py5.translate(py5.width/2, py5.height/2) py5.rotate(py5.PI/8 * i) py5.line(-100, 0, 100, 0) i += 1 py5.run_sketch() Es wird eine rotierende Linie gezeichnet (z.B. wie bei einem Installationsprozess verwendet - zeigt an, dass der Installationsfortschritt nicht eingefroren ist). Hierbei ist nur aufzupassen, dass der Hintergrund in <code>draw()</code> gesetzt wird, ansonsten würden die Linien additiv gezeichnet. Das ist zwar auch schön, aber eben nicht das gewünschte Resultat. Hiermit belassen wir es mit der py5-Bibliothek und wenden uns wieder "mathematischeren" Themen zu. = 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> standardmäßig aber sehr wohl. Jedoch kann man das bei <code>linspace</code> auch ändern. Dann ist der Stoppwert nicht mehr enthalten: linspace(start, stop, num, endpoint=False) == 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 === Beispiel: 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 Visualisierung: 0 1 2 +-----------+ 0 | 1 | 2 | 3 | +-----------+ 1 | 4 | 5 | '''6''' | +-----------+ Beispiel: import numpy as np A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] B = np.matrix(A) print(B[2::, ::2]) Ausgabe: [['20' '22'] ['30' '32'] ['40' '42']] Mit der NumPy-Bibliothek funktioniert das zweidimensionale Slicing. Mit normalen Listen/Tupels geht das nicht, was wir schon früher thematisiert haben. === 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]. Beispiel: import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: <Compressed Sparse Row sparse array of dtype 'float64' with 5 stored elements and shape (5, 5)> Coords Values (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 csr steht für '''c'''ompressed '''s'''parse '''r'''ow. Andere Möglichkeiten: * coo-Format ('''coo'''rdinate Format): <code>scipy.sparse.coo_array()</code> * lil-Format ('''li'''st of '''l'''ists): <code>scipy.sparse.lil_array()</code> * csc-Format ('''c'''ompressed '''s'''parse '''c'''olumn): <code>scipy.sparse.csc_array()</code> Es gibt auch <code>scipy.sparse.csr_matrix()</code> etc. Diese Befehle sind aber veraltet und es wird empfohlen, statt dessen die <code>..._array()</code>-Funktionen zu verwenden. <u>Aufgaben:</u> Recherchieren Sie im Internet * generell zum Thema "spärlich besetzte Matrizen mit Python". * Was sind die Vor-/Nachteile von csr-arrays gegenüber coo- und lil-arrays? * Wann verwendet man sinnvollerweise csr- und wann csc-arrays? * Wie wandelt man coo-arrays in csr-arrays um? = 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.] Siehe dazu auch das Kapitel [[Ing Mathematik: Numerisches Lösen von linearen Gleichungssystemen]]. Dort wird dieses Themengebiet u.a. mit weitergehenden Python-Skripten vertieft. == 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") <u>Aufgaben</u>: Ermitteln Sie mittels SymPy die <math>\mathcal{L}</math>-Transformierten und dann wieder die <math>\mathcal{L}^{-1}</math>-Transformationen (ähnlich wie beim obigen Beispiel): * <math>\sin(t) \circ\! -\! \bullet \ ?</math> * <math>t^3 \circ\! -\! \bullet \ ?</math> * <math>\text{e}^{\omega t} \circ\! -\! \bullet \ ?</math> =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 [https://mpmath.org/ 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). = pint - mit physikalischen Größen rechnen = [[File:Pint-Glas_UK.jpg|right|100px]] pint ist ein externes Python-Paket zur Manipulation von physikalischen Einheiten. Es muss vor der ersten Benutzung installiert werden (Vorgehensweise wie immer: pip, YaST, via venv etc.). Beispiel: import pint import numpy as np ureg = pint.UnitRegistry() # Einfache Berechnung print(3 * ureg.meter + 4 * ureg.cm) # Mit NumPy npeinzel = [3, 4] * ureg.meter + [2, 5] * ureg.cm print(npeinzel) npsum = np.sum(npeinzel) print(npsum) # Konvertierung von Einheiten npsum_inch = npsum.to(ureg.inch) print(npsum_inch) print(npeinzel.magnitude) print(npeinzel.units) print(npeinzel.dimensionality) Ausgabe: 3.04 meter [3.02 4.05] meter 7.07 meter 278.3464566929134 inch [3.02 4.05] meter [length] Die Dokumentation zu pint findet sich auf [https://pint.readthedocs.io/en/stable/]. =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. <small>Je größer die Anzahl der Datensätze n wird, umso weniger spielt das eine Rolle.</small> == 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. = Pandas kurz erklärt = Hier wird kurz auf das externe Modul Pandas eingegangen. Pandas steht für '''Pan'''el '''Da'''ta und ist ein Modul für die Datenanalyse und -manipulation. == Series == Vorstellbar ist eine "Series" wie eine einzelne Spalte in einem Tabellenkalkulationsprogramm. Beispiel (Index implizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) # Temperaturwerte print(temp) Ausgabe: 0 8.5 1 11.0 2 12.5 3 10.0 4 13.0 5 11.0 6 14.5 dtype: float64 Das Kürzel <code>pd</code> ist Konvention. Es seien Temperaturwerte gegeben. Die erste Spalte bei der Ausgabe ist der Index. Die zweite Spalte enthält die Werte. Beispiel (separater Zugriff auf den Index und auf die Werte): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) print(temp.index) print(temp.values) Ausgabe: RangeIndex(start=0, stop=7, step=1) [ 8.5 11. 12.5 10. 13. 11. 14.5] Beispiel (Index explizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) print("*** Die gesamte Series ***") print(temp) print("*** Temperatur am Donnerstag ***") print(temp["Do"]) print("*** Tage wärmer als 12 Grad ***") temp_gr_12 = temp[temp > 12] print(temp_gr_12) Ausgabe: *** Die gesamte Series *** Mo 8.5 Di 11.0 Mi 12.5 Do 10.0 Fr 13.0 Sa 11.0 So 14.5 dtype: float64 *** Temperatur am Donnerstag *** 10.0 *** Tage wärmer als 12 Grad *** Mi 12.5 Fr 13.0 So 14.5 dtype: float64 == DataFrame == Ein DataFrame entspricht einer Tabelle. Beispiel (Erzeugen eines DataFrames): import pandas as pd tag = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] v1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=tag) v2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=tag) v3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=tag) tab = pd.concat([v1, v2, v3], axis=1) tab.columns = stadt print(tab) print(type(tab)) Ausgabe: Linz Salzburg Innsbruck Mo 1 8 Hallo Di 2 9 16 Mi 3 10 17 Do 4 11 18 Fr 5 12 19 Sa 6 13 20 So 7 14 21 <class 'pandas.core.frame.DataFrame'> Wir haben hier drei Datenspalten (v1, v2, v3) mit den Spaltennamen "Linz", "Salzburg" und "Innsbruck", sowie sieben Zeilen "Mo" bis "So". Beispiel (Zugriff auf eine Spalte): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab["Salzburg"]) Ausgabe: Mo 8 Di 9 Mi 10 Do 11 Fr 12 Sa 13 So 14 Name: Salzburg, dtype: int64 Beispiel (Zugriff auf eine Zeile): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab.loc["Di"]) Ausgabe: Linz 2 Salzburg 9 Innsbruck 16 Name: Di, dtype: object == Grafiken erstellen == Hier sei nur ein Torten- oder Kreisdiagramm gezeichnet: import pandas as pd import matplotlib.pyplot as plt temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) expl = [0, 0.2, 0, 0, 0, 0, 0] temp.plot.pie(figsize=(5,5), explode=expl) plt.show() Ausgabe: [[Datei:IngPython_pandas1.svg|350px]] Die <code>figsize</code> hat nichts mit der Spaltenanzahl zu tun, sondern setzt die Fenster- oder Diagrammgröße in Inches (Breite * Höhe). Mit Pandas kann man auch noch andere Diagrammarten erstellen, z.B. Linien- oder Balkendiagramme. Dies wird hier aber nicht vorgeführt. == Arbeiten mit CSV- und LibreOffice-Dateien == Gegeben sei eine CSV-Datei test1.csv: Mai;Juni;Juli 12;17;45 15;65;65 16;32;23 4;14;7 23;17;Keine Daten verfügbar 67;Keine Daten verfügbar;45 15;24;12 Lesen von dieser CSV-Datei: import pandas as pd tab = pd.read_csv("test1.csv", sep=";") print(tab) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Schreiben in eine CSV-Datei test2.csv: import pandas as pd val = pd.read_csv("test1.csv", sep=";") val.to_csv("test2.csv", index=False) Ausgabedatei: Mai,Juni,Juli 12,17,45 15,65,65 16,32,23 4,14,7 23,17,Keine Daten verfügbar 67,Keine Daten verfügbar,45 15,24,12 Die Ausgabedatei ist beinahe gleich wie die Eingabedatei. Es werden aber Kommas statt Strichpunkten verwendet. Daher kommt auch der Name CSV ('''C'''omma '''S'''eparated '''V'''alue). Will man wieder Strichpunkte, so kann man dies über den Parameter <code>sep=";"</code> in der <code>to_csv</code>-Funktion machen. Um direkt von einer ODS-Datei (LibreOffice Calc, '''O'''pen '''D'''ocument '''S'''preadsheet) zu lesen muss das Modul odfpy extra installiert sein (per YaST, pip etc.). Gelesen wird dann folgendermaßen: import pandas as pd df = pd.read_excel('test1.ods', engine='odf') print(df) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Die Datei "test1.ods" sei die Datei, aus der das obige "test1.csv" generiert wurde. Schreiben kann man ebenfalls in eine ODS-Datei: import pandas as pd daten = { "Name": ["Hugo", "Richie", "Wilhelmine"], "Alter": [38, 30, 29], "Ort": ["Linz", "Innsbruck", "Salzburg"] } tab = pd.DataFrame(daten) with pd.ExcelWriter("test2.ods", engine="odf") as writer: tab.to_excel(writer, sheet_name="Daten", index=False) Es ergibt sich eine "test2.ods"-Datei (Screenshot): [[Datei:IngPython_pandas2.png|250px]] == Sonstiges == Hier seien stichpunktartig einige weitere Features von Pandas angeführt: * {{W|Pivot-Tabelle|Pivot-Tabellen}} (pivot ... Dreh-, Angelpunkt): Zweck - große Datenmengen schnell zusammenfassen, filtern und analysieren. * NaN ('''n'''ot '''a''' '''n'''umber): In einem Datensatz fehlt ein Wert oder ist nicht definiert. * groupby-Funktion: Daten strukturieren, aggregieren oder Zwischensummen bilden. * {{W|Binning#Datenverarbeitung_und_-analyse|Binning}}: Klassenbildung. * Mehrstufige Indizierung (Hierarchical Indexing oder MultiIndex) * Zeitreihen (Time Series) Für tiefergehende und weiterführende Informationen wird auf das Buch von ''Klein: Numerisches Python; Hanser, 2023'' und [https://pandas.pydata.org/ pandas] verwiesen. = 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) Das funktioniert übrigens nicht nur bei Funktionsannotationen, sondern auch mit Variablen etc. from typing import Final # "Konstante", ab Python 3.8 NUMBER: Final = 123 print(NUMBER) NUMBER = 125 print(NUMBER) # Variable a: int = 345 print(a) a = "Irgendwas" print(a) # Variable (Pipe-Symbol), ab Python 3.10 b : int|str = 567 print(b) b = "sonstiges" print(b) Ausgabe mit python: 123 125 345 Irgendwas 567 sonstiges Ausgabe mit mypy: test3.py:6: error: Cannot assign to final name "NUMBER" [misc] test3.py:12: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] Found 2 errors in 1 file (checked 1 source file) Python kennt übrigens keine Konstanten. Man kann diese mittels Großbuchstaben andeuten. Das ist aber nur Konvention, hat aber keine sonstigen Auswirkungen. Oder man verwendet wie oben gezeigt <code>Final</code>. Dann werfen Typchecker Errors aus. ==pydoc== pydoc ist ein Werkzeug, um Python-Module, -Funktionen und -Klassen zu dokumentieren. Beispiel <code>test1.py</code>: """Rechen-Modul. Funktionen die von pydoc gelesen werden. """ def multiply(a, b): """Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a * b def add(a, b): """Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a + b if __name__ == '__main__': print(multiply(3, 5)) print(add(4, 6)) Ausgabe mit <code>python3.13 -m pydoc test1</code> auf der Konsole: Help on module test1: NAME test1 - Rechen-Modul. DESCRIPTION Funktionen die von pydoc gelesen werden. FUNCTIONS add(a, b) Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl multiply(a, b) Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl FILE /home/xyz/tmp/test1.py Siehe zu diesem Thema z.B. auch [https://docs.python.org/3/library/pydoc.html]. Mittels <code>python3.13 -m pydoc -w test1</code> kann man auch eine HTML-Dokumentation erstellen. Wofür <code>if __name__ == '__main__':</code> steht, siehe z.B. etwas später im Abschnitt [[Ing_Mathematik:_Python#Doctests]]. == 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.3 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.5.1 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://build123d.readthedocs.io/en/latest/ build123d] * [https://docs.vtk.org/en/latest/api/python.html VTK] * [https://docs.bokeh.org/en/latest/docs/first_steps.html bokeh] * [https://py5coding.org/ py5] == 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] * [https://pint.readthedocs.io/en/stable/ pint] = 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] * Iacono et al.: Programmieren trainieren; 3. Aufl., Hanser, 2023, ISBN 978-3-446-47766-7 * 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}} 4lyje7dfnuukk3xctayc0xx8tnrso5f 1092069 1092068 2026-10-11T11:01:47Z Intruder 1513 /* Rekursive Funktionen */ verlinkt 1092069 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 (welche Verzeichnisse im PATH liegen, kann bspw. mit <code>echo $PATH</code> abgefragt werden). 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 == === Tests === 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) === Popularität === * An Beliebtheit rangiert Python mit Stand September 2026 mit einem Rating von 17,76% 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 Q1/2026 auf Rang 2, vor TypeScript und hinter JavaScript. * [https://pypl.github.io/PYPL.html PYPL] (Popularity of Programming Language Index) ist ein anderer Popularitätsindex für Programmiersprachen. Auch hier liegt Python mit großem Vorsprung auf dem ersten Platz (52,08%), gefolgt von Java (13,34%) und C/C++ (7,99%) (Stand September 2026). === Namensgebung === 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 in PyConUS24.jpg|{{W|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 <code>help</code> oder <code>help()</code> 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, bzw. der Eingabe von q, quit oder exit mit anschließendem Drücken der RETURN-Taste. == 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 Theoretisch kann man auch Umlaute, griechische Buchstaben u.ä. in Variablenbezeichnern nutzen, z.B. ößÜ = 16 α = 134 print(ößÜ) print(α) Ausgabe: 16 134 Davon wird aber aus offensichtlichen Gründen ({{W|Englisch als Lingua franca}}) abgeraten. Das Eurozeichen (€) etc. darf man ohnehin nicht in Variablenbezeichnern verwenden, dies ergäbe einen Syntaxfehler. Siehe dazu auch [https://peps.python.org/pep-3131/]. 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")) # new style string formatting print("Hallo {:s}" . format("Hugo")) # new style string formatting print("Hallo %s" % "Hugo") # %-String 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 f-Strings (ab Python 3.6) sind die modernere Variante gegenüber %-Strings oder das ''new style string formatting''. Sie sind in der Regel auch schneller. 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. Man beachte, dass {{W|Rundungsfehler}} auftreten können. D.h. man sollte bei der Verwendung von {{W|Gleitkommazahl|Gleitpunktzahlen}} niemals auf absolute Genauigkeit prüfen, sondern eine Toleranz (einen <math>\varepsilon</math>-Wert) vorgeben. Ein klassisches Beispiel, das ein falsches Ergebnis liefert, ist: i = 0.1 + 0.2 print(i) if i == 0.3: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 NOK Und das, obwohl man schon in der Volksschule lernt, dass 0.1 + 0.2 gleich 0.3 ist. Korrekt muss das obige Beispiel z.B. so umgeschrieben werden: ist = 0.1 + 0.2 soll = 0.3 epsilon = 0.000001 print(ist) if abs(ist-soll) < epsilon: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 OK Alternativ kann auch die <code>math.isclose()</code>-Funktion verwendet werden. Das sei aber hier nur erwähnt und nicht konkret ausgeführt. <u>Aufgabe:</u> Recherchieren Sie im Internet zum Thema <code>math.isclose()</code>-Funktion. === 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 == Kopfgesteuerte Schleife, Endlosschleife und fußgesteuerte Schleife: [[File:Programmingloops.svg|450px]] === 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 Der <code>break</code>-Befehl wird etwas später noch einmal kurz thematisiert. === 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!") print("Setze Programm fort!") Ausgabe: Das Ergebnis ist unendlich Setze Programm fort! 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). Danach wird das Programm fortgesetzt. 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. Neu in Python 3.14 ist, dass mehrere Ausnahmen auch ohne Klammern angegeben werden können, z.B.: bis inkl. Python 3.13 nur: except (ZeroDivisionError, ValueError): ab Python 3.14 auch: except ZeroDivisionError, ValueError: == 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 c, d = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(c) print(d) Ausgabe: 8 15 Es werden r1 und r2 zurückgegeben und den Variablen c und d zugewiesen. (r1, r2) und (c, d) sind Tupel. Was Tupel konkret sind, siehe etwas später in diesem Kapitel. 3 und 5 sind die aktuellen Parameter (die Argumente). a und b sind die formalen Parameter. Die Zuweisung der aktuellen zu den formalen Parametern erfolgt in der gegebenen Reihenfolge, also die Zahl 3 wird an a übergeben und die Zahl 5 an b. Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 c, d = mathFunc(3, 5) print(c) print(d) e, f = mathFunc(5) print(e) print(f) g, h = mathFunc(b=6) print(g) print(h) Ausgabe: 8 15 25 100 16 60 Erklärung: * Erster <code>mathFunc</code>-Aufruf: Es werden, wie weiter oben, die Argumente 3 und 5 an die formalen Parameter a und b übergeben. Die Rückgabewerte r1 und r2 werden an c und d übergeben. Somit ist c=8 und d=15. * Zweiter <code>mathFunc</code>-Aufruf: Es wird nur das Argment 5 an a übergeben. b behält den Vorgabewert 20. D.h. e= 25 und f=100. * Dritter <code>mathFunc</code>-Aufruf: Es wird nur die Zahl 6 an b übergeben, a behält seinen Vorgabewert 10. Somit ergibt sich g=16 und h=60. Beispiel (Schlüsselwortargumente vs. Positionsargumente): def mathFunc(a, b): r1 = a - b r2 = a / b return r1, r2 c, d = mathFunc(b=3, a=5) # Schlüsselwortargumente e, f = mathFunc(3, 5) # Positionsargumente print(c) print(d) print(e) print(f) Ausgabe: 2 1.6666666666666667 -2 0.6 Durch die Verwendung von Schlüsselwortargumenten können die Argumente in beliebiger Reihenfolge angegeben werden. Bei den Positionsargumenten ist die Reihenfolge vorgegeben. Daraus resultiert auch der Unterschied bei den obigen Ausgaben. Werden beide Arten in einem Aufruf gemischt, so müssen die Positionsargumente zuerst angegeben werden. Den Unterschied zwischen beiden Argumentarten zu kennen ist wichtig, nicht nur bei selbst erstellten Funktionen. Auch beim Aufruf von Python-Funktionen verfolgt einen die Mischung von Schlüsselwort- und Positionsargumenten auf Schritt und Tritt, z.B. beim etwas später folgenden Abschnitt über "Grafiken zeichnen": plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") Bei dieser <code>plot</code>-Funktion aus dem Modul <code>matplotlib.pyplot</code> sind <code>x</code> und <code>y1</code> Positionsargumente. <code>label, lw</code> und <code>ls</code> sind die Namen der Schlüsselwortargumente. Die Schlüsselwortargumente könnten auch in anderer Reihenfolge angeschrieben werden, nicht jedoch die Positionsargumente! Siehe zu <code>matplotlib.pyplot.plot</code> auch die Seite [https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html]. == 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 {{W|Rekursion|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 und Listen == 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. 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. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. # 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 (eine Liste von Wörtern zu einem einzigen zusammenhängenden Text verbinden): woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt == Dictionaries und Mengen == 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'> Leere Mengen lassen sich mit <code>set()</code> erzeugen: menge = set() # leere Menge print(menge) menge.add("Hallo") # Element hinzufügen print(menge) Ausgabe: set() {'Hallo'} Beispiel (einige Mengenoperationen): mengeA = {"Hallo", "Welt"} mengeB = {1, 2, "Welt", 3} # Vereinigung print(mengeA | mengeB) print(mengeA.union(mengeB)) # Durchschnitt print(mengeA & mengeB) print(mengeA.intersection(mengeB)) # Differenz print(mengeA - mengeB) print(mengeA.difference(mengeB)) # Symmetrische Differenz print(mengeA ^ mengeB) print(mengeA.symmetric_difference(mengeB)) # Teilmenge print(mengeA.issubset(mengeB)) # Mehrere Elemente hinzu mengeA.update(["Super", 67, 125]) print(mengeA) # Element löschen mengeA.discard("Hallo") print(mengeA) Ausgabe: {1, 2, 3, 'Welt', 'Hallo'} {1, 2, 3, 'Welt', 'Hallo'} {'Welt'} {'Welt'} {'Hallo'} {'Hallo'} {1, 2, 3, 'Hallo'} {1, 2, 3, 'Hallo'} False {67, 'Super', 'Welt', 'Hallo', 125} {67, 'Super', 'Welt', 125} Es gibt schon seit "ewigen" Zeiten unveränderbare Mengen, die mittels <code>frozenset</code> eingeleitet werden. Neu ab Python 3.15 ist dagegen das Pendant <code>frozendict</code>, das unveränderbare Dictionaries darstellt. fdict = frozendict({"vorname":"Hugo", "nachname":"Meister"}) fset = frozenset({"A", 1, "A"}) print(fdict) print(fdict["vorname"]) print (fset) try: fdict["vorname"] = "Max" print(fdict["vorname"]) except TypeError as e: print(f"Fehler: {e}") try: fset.add("B") print(fset) except AttributeError as e: print(f"Fehler: {e}") Ausgabe: frozendict({'vorname': 'Hugo', 'nachname': 'Meister'}) Hugo frozenset({1, 'A'}) Fehler: 'frozendict' object does not support item assignment Fehler: 'frozenset' object has no attribute 'add' == 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: x = ("a", "b", "c", "d", "e") print(x[2]) print(x[-3]) print(x) print(x[:]) print(x[2:]) print(x[:2]) print(x[::-1]) Ausgabe: c c ('a', 'b', 'c', 'd', 'e') ('a', 'b', 'c', 'd', 'e') ('c', 'd', 'e') ('a', 'b') ('e', 'd', 'c', 'b', 'a') Visualisierung: '''a''' '''b''' '''c''' '''d''' '''e''' +-----------------------------+ | 0 | 1 |//'''2'''//| 3 | 4 | +-----------------------------+ | -5 | -4 |/'''-3'''//| -2 | -1 | +-----------------------------+ |/////////////////////////////| +-----------------------------+ |/////////////''':'''///////////////| +-----------------------------+ | | |///////'''2:'''////////| +-----------------------------+ |////''':2'''/////| | | | +-----------------------------+ |\\\\\<--\\\\\'''::-1'''\\\\<--\\\\\| +-----------------------------+ Beispiel: x = ("a", "b", "c", "d", "e") print(x[2::2]) print(x[2::-1]) print(x[2:0:-1]) Ausgabe: ('c', 'e') ('c', 'b', 'a') ('c', 'b') Hier sieht man sehr schön, dass beim letzen <code>print</code>-Befehl die 0, die in diesem Fall den Buchstaben "a" repräsentiert, als letzer Wert nicht mehr dazugehört (wie weiter oben angedeutet ist der End- oder Stoppwert exklusiv). Will man den Buchstaben "a" dazunehmen, so ist die vorletzte Zeile die richtige. Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH Mit zweidimensionalen Listen/Tupels wird das Ganze schon bedeutend komplexer. Beispiel: Verkettetes Slicing A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] print(A[2::][::2]) Ausgabe: [['20', '21', '22', '23'], ['40', '41', '42', '43']] Das erste Slicing erstellt folgenden Slice: [["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] Das zweite Slicing wird dann auf diesen Slice angewendet und ergibt das genannte Endergebnis. Will man Spalten statt Zeilen, so funktioniert dieser direkte Weg nicht. Man verwendet dann List Comprehensions oder die NumPy-Bibliothek. Diese Bibliothek erlaubt zweidimensionales Slicing. Sie wird erst später bei den Grafiken eingeführt. Deshalb wird hier dazu noch kein Beispiel gezeigt. = 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 (ist der sogenannte Konstruktor). 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 strikt/stark private ist. Wird nur ein Unterstrich verwendet, so deutet dies auf schwach/weakly private hin. Letzteres wird in Python deutlich häufiger verwendet. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. Besonderheiten bei stark und schwach private: * '''stark private''': ** zwei führende Unterstriche ** Name Mangling: Name wird zu <code>_Klassenname__variablenname</code> ** Schutz vor Namenskollisionen bei Vererbung ** am Ende des Namens dürfen keine zwei Unterstriche verwendet werden. Das wären dann Magic Methods oder Dunder-Methods (z.B. __init__). Diese sind public. * '''schwach private''': ** ein führender Unterstrich ** Name bleibt gleich ** ist nur ein Hinweis an die Programmierer, kein starker Schutz ** <code>from modul import *</code>: Objekte mit einem führenden Unterstrich werden standardmäßig nicht importiert ** Standard für die Kapselung bei Python. Schätzung: Weit mehr als 80% der nicht-öffentlichen Attribute/Methoden werden als schwach private deklariert. Weniger als 5-10% als stark private (Quelle: Google KI) == 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 == === Einfachvererbung === [[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 === Mehrfachvererbung === Mit Python ist auch Mehrfachvererbung möglich, z.B.: class A: def _printA(self): print("Klasse A") class B: def _printB(self): print("Klasse B") class C(A,B): def __init__(self): self._printA() self._printB() c = C() Ausgabe: Klasse A Klasse B Die Klasse C erbt von A und B. In UML sieht das in etwa so aus: +-----------------------------+ +-------------------------+ | A | | B | +-----------------------------+ +-------------------------+ | | | | +-----------------------------+ +-------------------------+ | # printA() | | # printB() | +-----------------------------+ +-------------------------+ . . / \ / \ --- --- | | | | +---------------------+ | C | +---------------------+ | | +---------------------+ | | +---------------------+ <code>printA()</code> und <code>printB()</code> sind schwach private (in UML als <code>protected</code> mit einem # dargestellt). Es ist aber zu beachten, dass schwach private nur Konvention ist. Praktisch könnte man auch von außerhalb auf diese Methoden zugreifen. Das soll man aber nicht tun, und das wird in Python durch einen Unterstrich angedeutet. == Statische Attribute, statische Methoden und Klassenmethoden == Beispiel: class Math: pi = 3.14159265 cnt = 0 @staticmethod def expo(): return 2.71828183 @classmethod def counterPlus(cls): cls.cnt += 1 print(Math.pi) print(Math.expo()) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Ausgabe: 3.14159265 2.71828183 1 2 3 Statische Attribute und Methoden, bzw. Klassenmethoden, sind direkt einer Klasse zugeordnet und nicht einem Objekt. Der Unterschied zwischen statischen Methoden und Klassenmethoden ist, dass statische Methoden nicht auf Klassenattribute (bzw. statische Attribute) zugreifen können, Klassenmethoden aber schon. <code>pi</code> und <code>cnt</code> sind statische Attribute. <code>expo()</code> ist eine statische Methode. <code>counterPlus(cls)</code> ist eine Klassenmethode. Die Bezeichnung des Parameters als <code>cls</code> ist reine Konvention, man sollte sich aber daran halten. = 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. Die "arange"-Funktion aus der NumPy-Bibliothek wird später ein bisschen genauer behandelt (siehe [[Ing_Mathematik:_Python#Zahlenfolgen|Zahlenfolgen]]). <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. Die Farbe des Graphen kann man über den <code>color</code>-Schalter einstellen, z.B. auf rot setzen: plt.plot(x, y, color="red") Das ist nur eine Möglichkeit, es gibt auch andere, z.B. mittels * hexadezimaler Zahl, z.B. <code>color="#ff0000"</code> * Kurzform, z.B. <code>"r"</code> Statt <code>color</code> kann auch die Kurzform <code>c</code> verwendet werden. <u>Kleine Aufgabe:</u> Probieren Sie diese "Farbspielereien" am Computer aus. === Graphen mehrerer Funktionen und weiteres === ==== Mehrere Funktionen in einen Plot zeichnen ==== 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]] Einige weitere Bespiele zu <code>ls</code></code>: * - ... durchgezogene Linie * -. ... Strichpunkt-Linie * dashed ... gestrichelte Linie ==== Marker ==== Desweiteren kann man Marker verwenden. Beispiel: import matplotlib.pyplot as plt plt.plot(10, 10, "ob", markersize=30) plt.plot(20,20, ".r") plt.plot(30,30, "Dg", ms=15) plt.plot(40, 40, "ok", ms=20, mec="#FF5733", mew=3) plt.plot(50, 50, "vk", ms=20, mec="yellow", mew=3) plt.grid() plt.show() Das erste Zeichen im String nach den Koordinaten im plot-Befehl symbolisiert die Form (z.B. o für einen Kreis). Das zweite Zeichen steht für die Farbe (z.B. b für blue). <code>markersize</code> oder <code>ms</code> setzt die Größe des Markers. Ausgabe: [[Bild:PythonIng_formatpara1.svg|200px]] Einige weitere Marker: * * ... Stern * + ... Plus * h oder H ... Sechseck Man kann mit den Parametern * <code>markeredgecolor</code> oder kurz <code>mec</code> die Farbe des Markerrandes * <code>markeredgewidth</code> oder kurz <code>mew</code> die Dicke des Randes * <code>markerfacecolor</code> oder kurz <code>mfc</code> die Innenfarbe des Markers bestimmen. === 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]] === Anmerkungen hinzufügen === Anmerkungen (engl. annotations) lassen sich mit matplotlib einfach hinzufügen. Unter Anmerkung wird hier ein Pfeil mit Text verstanden. Beispiel: import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot([1,10], [1,10]) ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0)) plt.show() Ausgabe: [[Datei: PythonIng_ann1.svg|400px]] <code>shrink</code> verkürzt den Pfeil. Sinnvoll sind beim obigen Beispiel Werte irgendwo zwischen 0.0 und 0.3. <u>Kleine Aufgabe:</u> Probieren Sie verschiedene Pfeillängen aus. Die Schriftgröße verändern können Sie übrigens mit <code>fontsize</code> oder <code>size</code>, z.B. ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0), fontsize=25) Eine andere Schreibweise für Dictionaries, die wir weiter oben schon kennengelernt haben, die wir aber nochmals explizit aufschreiben wollen, ist ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops={"facecolor":"red", "shrink":0.0}) === Figure, Axes etc.=== [[Bild:PythonIng_figure33.svg|350px]] * fig (figure) ... das übergeordnete Fenster * ax (axes) ... "Plot", das eigentliche Zeichenfenster * axis ... die Koordinatenachsen * spine ... Begrenzungslinien des Zeichenfensters * tick ... Markierung an den Achsen des Zeichenfensters Beispiel (eine figure mit 6 axes, ax-Titel, y-axis-Titel): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=3, ncols=2) ax[0][0].text(0.1, 0.5, "11") ax[0][1].text(0.1, 0.5, "12") ax[1][0].text(0.1, 0.5, "21") ax[1][1].text(0.1, 0.5, "22") ax[2][0].text(0.1, 0.5, "31") ax[2][1].text(0.1, 0.5, "32") ax[0][0].set(ylabel="y-label", title="axes 1") plt.show() [[Bild:PythonIng_figure34.png|350px]] Beispiel (Achsenbereiche setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-1, 10, 1, 12)) plt.show() [[Bild:PythonIng_figure35.svg|350px]] Beispiel (Gitter einschalten): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-5, 10, 1, 12)) ax[0].minorticks_on() ax[0].grid(which='minor') ax[1].grid(which='major') plt.show() [[Bild:PythonIng_figure36.svg|350px]] Beispiel (ticks setzen) import matplotlib.pyplot as plt import matplotlib.ticker as tick fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") pos = [0, .2, .5] lab = ["a", "b", "c"] ax.xaxis.set_major_locator(tick.FixedLocator(pos)) ax.xaxis.set_major_formatter(tick.FixedFormatter(lab)) plt.show() [[Bild:PythonIng_figure37.svg|350px]] Beispiel (Achsenkreuz beliebig setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") ax.spines['top'].set_color('none') ax.spines['right'].set_color('none') ax.spines['bottom'].set_position(('data', 0.3)) ax.spines['left'].set_position(('data', 0.5)) plt.show() [[Bild:PythonIng_figure38.svg|350px]] Beispiel (Inset): import matplotlib.pyplot as plt # Hauptplot erstellen fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") # Inset hinzu: axin = ax.inset_axes([0.4, 0.4, 0.3, 0.3]) axin.text(0, 0, "Servus") axin.plot() plt.show() [[Bild:PythonIng_figure40.svg|350px]] Beispiel (Hintergrundfarben setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.set_facecolor("green") fig.set_facecolor("yellow") plt.plot([1, 3], [6, 8]) plt.show() [[Bild:PythonIng_figure39.svg|300px]] === GridSpec === Mit figures und axes lassen sich schon jede Menge an Grafikdetails einstellen. GridSpec ist dann der Turbo oder Raketenantrieb den matplotlib eingebaut hat. Naja, vielleicht ist das etwas übertrieben, aber GridSpec ist sozusagen ein flexibler Layout-Manager. Bisher haben wir immer gleich große axes gezeichnet. Mit GridSpec können wir Subplots über mehrere Zeilen und Spalten ziehen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) gs = fig.add_gridspec(3, 3) ax1 = fig.add_subplot(gs[0, :]) ax1.set_title("Bereich 1: gs[0, :]") ax2 = fig.add_subplot(gs[1:, 0]) ax2.set_title("Bereich 2: gs[1:, 0]") ax3 = fig.add_subplot(gs[1, 1:]) ax3.set_title("Bereich 3: gs[1, 1:]") ax4 = fig.add_subplot(gs[2, 1:]) ax4.set_title("Bereich 4: gs[2, 1:]") plt.show() [[Bild:PythonIng_gridspec1.svg|350px]] Erklärung zu <code>(gs[zeile, spalte])</code>: Slicing; damit legen Sie fest, welche Zellen ein Subplot belegt. Achtung! Die Indizes beginnen bei 0 und nicht bei 1 (d.h., die erste Zeile/Spalte hat den Index 0) * : ... alle * 1: ... 1 bis Ende * etc. <code>constrained_layout=True</code> sorgt dafür, dass sich die Subplots nicht überlappen. Aber auch die Spaltenbreiten und Zeilenhöhen lassen sich anpassen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) # Verhältnis erste zu zweite Zeile = 1.5:1 # Verhältnis erste zu zweite Spalte = 1:4 gs = fig.add_gridspec(2, 2, figure=fig, height_ratios=[1.5, 1], width_ratios=[1, 4]) ax1 = fig.add_subplot(gs[0, 0]) ax1.set_title("Bereich 1: gs[0, 0]") ax2 = fig.add_subplot(gs[0, 1]) ax2.set_title("Bereich 2: gs[0, 0]") ax3 = fig.add_subplot(gs[1, 0]) ax3.set_title("Bereich 3: gs[1, 0]") ax4 = fig.add_subplot(gs[1, 1:]) ax4.set_title("Bereich 4: gs[1, 1]") plt.show() [[Bild:PythonIng_gridspec2.svg|350px]] Auch die Ränder lassen sich einstellen. Beispiel 1: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.2, hspace=0.2) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec3.svg|300px]] * hspace ... height space, vertikaler Abstand zwischen den Zeilen * wspace ... width space, horizontaler Abstand zwischen den Spalten Und im Vergleich dazu Beispiel 2: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.3, right=0.7, bottom=0.3, top=0.7, wspace=0.3, hspace=0.3) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec4.svg|300px]] Es gibt noch einige weitere Details, die sich mit GridSpec variieren lassen (z.B. SubGridSpec ... verschachtelte Grid-Layouts). Aber wir belassen es vorerst dabei. Das bisher Gezeigte sollte für die meisten Probleme ausreichend sein. === 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|300px|center]] * 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|300px|center]] * 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. * Zeichnen Sie folgendes Bild (die Achsenbeschriftungen positionieren Sie z.B. mit den Befehlen <code>ax.set_xlabel("x", loc="right"); ax.set_ylabel("y", loc="top")</code>): [[Bild:PythonIng_aufgabe24.svg|400px|center]] * Zeichnen Sie mit Python, matplotlib und NumPy folgendes Bild: [[Bild:PythonIng_figure41.svg|400px|center]] * Zeichnen Sie mit GridSpec ein Layout, das in etwa wie folgendes aussieht (<code>height_ratios=[1, 2], width_ratios=[2, 1]</code>): [[Bild:PythonIng_aufgabe23.svg|300px|center]] == 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 und fortgeschrittenes 3D == === 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 build123d === Hiermit lassen sich einfach 3D-Modelle für CAD-Systeme erstellen. [https://build123d.readthedocs.io/en/latest/ build123d] ist ein externes Modul und muss vorab, wie schon für mehrere andere Module gezeigt, installiert werden (pip, venv etc.). Beispielcode: from build123d import * # Bauteil with BuildPart() as bauteil: # Zylinder erstellen Cylinder(100, 60) # Bohrung with BuildSketch(bauteil.faces().sort_by(Axis.Z)[-1]) as loch: Circle(radius=10) # negativ extrudieren extrude(amount=-60, mode=Mode.SUBTRACT) # Dem Bauteil z.B. die Farbe rot zuweisen # bauteil.part.color = Color("red", alpha=1.0) # Fertiges Modell exportieren export_step(bauteil.part, "bauteil.step") Ausgabe ({{W|Standard for the exchange of product model data | STEP}}-Datei, z.B. in {{W|FreeCAD}} importiert, Screenshot): [[Datei:PythonIng_build123d.jpg | 300px]] FreeCAD ist ein freies 3D-CAD-System. Die [[Ing_Mathematik:_Python#FreeCAD | Makroprogrammierung]] in diesem System wird später noch gezeigt. STEP steht für "'''St'''andard for the '''e'''xchange of '''p'''roduct model data" und ist ein Datenaustauschformat. === 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> == Sonstige Grafikbibliotheken == === Py5 === Im Buch ''Iacono et al.: Programmieren trainieren, mit über 150 Workouts in Java und Python; 3. Aufl., Hanser, 2023'' werden zahlreiche Aufgaben gestellt, die u.a. mit Processing zu lösen sind. Processing ist eine einfache Programmiersprache und Entwicklungsumgebung. Warum ich das erwähne? Weil in älteren Processing-Versionen auch die Programmiersprache Python 2 abgedeckt war. Bei neueren Versionen gibt es keine Python-Anbindung mehr (getestet nur mit openSUSE Tumbleweed und Python 3). Somit funktioniert der Python-Teil im genannten Buch zum Teil nicht mehr. Ist das tragisch, oder kann das Buch weggeschmissen werden? Nein! Es gibt Alternativen zu Processing. Zum einen die im Buch selbst genannten Bibliotheken tkinter oder pygame. Pygame wird dort auch kurz anhand eines Beispiels thematisiert. Aber noch besser ist es, die py5-Bibliothek zu verwenden (lt. Google KI vom 30. Sept. 2026: ''Der echte, moderne „spirituelle Nachfolger“''). Damit ist die Funktionalität von Processing gewährleistet. Man kann aber beliebige Editoren oder IDEs zur Programmerstellung verwenden, ohne an Processing als IDE gebunden zu sein. Nachfolgend werden ein paar Beispiele zu dieser externen Bibliothek gegeben. Der erste Schritt ist wieder, die py5-Bibliothek zu installieren (z.B. mittels pip in eine virtuelle Umgebung; Python>=3.10 und ein JDK>=17 müssen auch installiert sein (Stand Okt. 2026)). Dann kann es schon mit dem Programmieren losgehen. Beispiel (angelehnt an die ''Aufgabe W.2.3 Haus'' des genannten Buchs, jedoch etwas erweitert): import py5 def setup(): py5.size(600, 600) # Fenstergröße 600px x 600px py5.background(255, 255, 255) # Hintergrundfarbe ... weiss def draw(): # Haus zeichnen py5.fill(255, 255, 255) # Hintergrundfarbe ... weiss py5.stroke(255, 0, 0) # Linienfarbe ... rot py5.stroke_weight(3) # Linienbreite ... 3px py5.rect(150, 200, 300, 300) # Rechteck zeichnen (x, y, b, h) py5.triangle(150, 200, 300, 100, 450, 200) # Dreieck zeichnen py5.circle(250, 300, 70) # Kreis zeichnen (x, y, d) # Bemassung zeichnen py5.stroke(0, 255, 0) py5.stroke_weight(1) py5.line(300, 100, 550, 100) # Linie zeichnen py5.line(450, 200, 550, 200) py5.line(450, 500, 550, 500) py5.line(150, 500, 150, 550) py5.line(450, 500, 450, 550) py5.text_size(20) # Textgroesse py5.fill(0, 255, 0) # Textfarbe py5.text("300", 280, 550) # Text zeichnen py5.text("100", 500, 150) py5.text("300", 500, 350) # Gedrehtes Rechteck zeichnen py5.stroke(255, 255, 0) py5.stroke_weight(3) py5.fill(200, 200, 200) py5.translate(320, 260) py5.rotate(0.3) # Drehung (in Radiant) py5.rect(0, 0, 100, 50) # Startet den Sketch py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_1.png|300px]] Das skizzierte Haus ist mit einer rudimentären Bemaßung versehen und hat zwei Fenster, ein rundes und ein gedrehtes, trübes rechteckiges. Beispiel (Mausoperationen): import py5 def setup(): py5.size(600, 600) py5.background(255, 255, 255) def draw(): pass def mouse_pressed(): py5.text_size(15) py5.fill(0, 0, 255) x = py5.mouse_x y = py5.mouse_y py5.text("(%d, %d)" %(x, y), x, y) # Positionsdaten bei Maus-Klick schreiben py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_2.png|300px]] Obiges Bild zeigt das Resultat nach einigen Mausklicks. Man erkennt, dass links-oben der Koordinatennullpunkt ist. Die x-Achse zeigt nach rechts, die y-Achse nach unten. Beispiel (Fünfeck): import py5 import random def setup(): py5.size(400, 400) py5.no_loop() # statisches Bild (schont die Ressourcen) def draw(): py5.translate(py5.width / 2, py5.height / 2) # KS setzen radius = 150 eck_anzahl = 5 py5.fill(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # Füllfarbe auf Zufallswert setzen py5.stroke(155, 0, 0) py5.stroke_weight(5) # Fünfeck als Polygon zeichnen py5.begin_shape() for i in range(eck_anzahl): winkel = i * py5.TWO_PI / eck_anzahl - py5.HALF_PI x = py5.cos(winkel) * radius y = py5.sin(winkel) * radius py5.vertex(x, y) py5.end_shape(py5.CLOSE) py5.run_sketch() Ausgabe, bspw.: [[Datei:PythonIng_py5_5.png|200px]] <u>Kleine Aufgaben:</u> * Um die Funktionsweise von <code>py5.no_loop()</code> zu verstehen, kommentieren Sie die Anweisung im Beispiel einfach aus, und starten Sie das Programm erneut. * Zeichnen Sie ein Siebeneck. * Was passiert, wenn man <code>py5.CLOSE</code> im <code>py5.end_shape()</code>-Befehl weglässt? Beispiel (Grafik laden und zeichnen): import py5 image1 = None def setup(): global image1 py5.size(600, 600) py5.no_loop() image1 = py5.load_image("PythonIng_py5_1.png") def draw(): py5.image(image1, 0, 0) py5.run_sketch() Hier wird einfach das Bild vom Haus-Beispiel (Screenshot von weiter oben) geladen und gezeichnet. Beispiel (angelehnt an ''W.5.10 Wurfparabel'' des genannten Buches; mit OOP erstellt): import py5 class Wurfparabel(py5.Sketch): def __init__(self, v0, angle): super().__init__() self.v0 = v0 self.angle = self.radians(angle) self.g = 9.81 self.t = 0.0 self.scale = 1.5 def settings(self): self.size(800, 600) def setup(self): self.background(0, 0, 0) def draw(self): self.t += 0.1 self.translate(50, 500) # KS Zeichnen self.fill(255, 255, 0) self.stroke(255, 255, 0) self.stroke_weight(1) self.line(0, 0, 0, -300) self.line(0, 0, 700, 0) # Punkt zeichnen cx = self.v0 * self.cos(self.angle) * self.t * self.scale cy = -(self.v0 * self.sin(self.angle) * self.t - \ self.g/2 * self.t**2) * self.scale self.fill(255, 0, 0) self.stroke(255, 0, 0) self.circle(cx, cy, 3) # Stoppen bei Bodenkontakt if cy >= -5: self.no_loop() def wurfparabel(v0=50, angle=45): sketch = Wurfparabel(v0, angle) sketch.run_sketch() v0 = float(input("v0 = ")) winkel = float(input("winkel = ")) wurfparabel(v0=v0, angle=winkel) Eingabe auf der Konsole: v0 = 60 winkel = 45 Ausgabe: [[Datei:PythonIng_py5_13.png|200px]] Die benötigten Formeln der Bewegungsgleichung kann man leicht herleiten, bzw. man benutzt folgende Grafik als Hilfestellung: [[Datei:Wurfparabel_Zusammenfassung_aktualisierung.png|700px]] <u>Aufgabe:</u> Berechnen Sie den Scheitelpunkt und die Wurfweite bei gegebener Anfangsgeschwindigkeit und gegebenem Abwurfwinkel. Die Abwurfhöhe <math>y_0</math> sei 0. Der Wurf soll auf der Erde stattfinden (g = 9.81 m/s²). Die dazu benötigten Formeln können aus obiger Grafik entnommen werden (oder Sie leiten sie selbst her). Geben Sie die Ergebnisse auf der Konsole aus. Beispiel (eine einfache Animation): import py5 i = 0 def setup(): py5.size(600, 600) py5.frame_rate(5) py5.stroke_weight(20) def draw(): global i py5.background(255, 255, 255) py5.translate(py5.width/2, py5.height/2) py5.rotate(py5.PI/8 * i) py5.line(-100, 0, 100, 0) i += 1 py5.run_sketch() Es wird eine rotierende Linie gezeichnet (z.B. wie bei einem Installationsprozess verwendet - zeigt an, dass der Installationsfortschritt nicht eingefroren ist). Hierbei ist nur aufzupassen, dass der Hintergrund in <code>draw()</code> gesetzt wird, ansonsten würden die Linien additiv gezeichnet. Das ist zwar auch schön, aber eben nicht das gewünschte Resultat. Hiermit belassen wir es mit der py5-Bibliothek und wenden uns wieder "mathematischeren" Themen zu. = 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> standardmäßig aber sehr wohl. Jedoch kann man das bei <code>linspace</code> auch ändern. Dann ist der Stoppwert nicht mehr enthalten: linspace(start, stop, num, endpoint=False) == 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 === Beispiel: 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 Visualisierung: 0 1 2 +-----------+ 0 | 1 | 2 | 3 | +-----------+ 1 | 4 | 5 | '''6''' | +-----------+ Beispiel: import numpy as np A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] B = np.matrix(A) print(B[2::, ::2]) Ausgabe: [['20' '22'] ['30' '32'] ['40' '42']] Mit der NumPy-Bibliothek funktioniert das zweidimensionale Slicing. Mit normalen Listen/Tupels geht das nicht, was wir schon früher thematisiert haben. === 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]. Beispiel: import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: <Compressed Sparse Row sparse array of dtype 'float64' with 5 stored elements and shape (5, 5)> Coords Values (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 csr steht für '''c'''ompressed '''s'''parse '''r'''ow. Andere Möglichkeiten: * coo-Format ('''coo'''rdinate Format): <code>scipy.sparse.coo_array()</code> * lil-Format ('''li'''st of '''l'''ists): <code>scipy.sparse.lil_array()</code> * csc-Format ('''c'''ompressed '''s'''parse '''c'''olumn): <code>scipy.sparse.csc_array()</code> Es gibt auch <code>scipy.sparse.csr_matrix()</code> etc. Diese Befehle sind aber veraltet und es wird empfohlen, statt dessen die <code>..._array()</code>-Funktionen zu verwenden. <u>Aufgaben:</u> Recherchieren Sie im Internet * generell zum Thema "spärlich besetzte Matrizen mit Python". * Was sind die Vor-/Nachteile von csr-arrays gegenüber coo- und lil-arrays? * Wann verwendet man sinnvollerweise csr- und wann csc-arrays? * Wie wandelt man coo-arrays in csr-arrays um? = 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.] Siehe dazu auch das Kapitel [[Ing Mathematik: Numerisches Lösen von linearen Gleichungssystemen]]. Dort wird dieses Themengebiet u.a. mit weitergehenden Python-Skripten vertieft. == 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") <u>Aufgaben</u>: Ermitteln Sie mittels SymPy die <math>\mathcal{L}</math>-Transformierten und dann wieder die <math>\mathcal{L}^{-1}</math>-Transformationen (ähnlich wie beim obigen Beispiel): * <math>\sin(t) \circ\! -\! \bullet \ ?</math> * <math>t^3 \circ\! -\! \bullet \ ?</math> * <math>\text{e}^{\omega t} \circ\! -\! \bullet \ ?</math> =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 [https://mpmath.org/ 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). = pint - mit physikalischen Größen rechnen = [[File:Pint-Glas_UK.jpg|right|100px]] pint ist ein externes Python-Paket zur Manipulation von physikalischen Einheiten. Es muss vor der ersten Benutzung installiert werden (Vorgehensweise wie immer: pip, YaST, via venv etc.). Beispiel: import pint import numpy as np ureg = pint.UnitRegistry() # Einfache Berechnung print(3 * ureg.meter + 4 * ureg.cm) # Mit NumPy npeinzel = [3, 4] * ureg.meter + [2, 5] * ureg.cm print(npeinzel) npsum = np.sum(npeinzel) print(npsum) # Konvertierung von Einheiten npsum_inch = npsum.to(ureg.inch) print(npsum_inch) print(npeinzel.magnitude) print(npeinzel.units) print(npeinzel.dimensionality) Ausgabe: 3.04 meter [3.02 4.05] meter 7.07 meter 278.3464566929134 inch [3.02 4.05] meter [length] Die Dokumentation zu pint findet sich auf [https://pint.readthedocs.io/en/stable/]. =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. <small>Je größer die Anzahl der Datensätze n wird, umso weniger spielt das eine Rolle.</small> == 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. = Pandas kurz erklärt = Hier wird kurz auf das externe Modul Pandas eingegangen. Pandas steht für '''Pan'''el '''Da'''ta und ist ein Modul für die Datenanalyse und -manipulation. == Series == Vorstellbar ist eine "Series" wie eine einzelne Spalte in einem Tabellenkalkulationsprogramm. Beispiel (Index implizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) # Temperaturwerte print(temp) Ausgabe: 0 8.5 1 11.0 2 12.5 3 10.0 4 13.0 5 11.0 6 14.5 dtype: float64 Das Kürzel <code>pd</code> ist Konvention. Es seien Temperaturwerte gegeben. Die erste Spalte bei der Ausgabe ist der Index. Die zweite Spalte enthält die Werte. Beispiel (separater Zugriff auf den Index und auf die Werte): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) print(temp.index) print(temp.values) Ausgabe: RangeIndex(start=0, stop=7, step=1) [ 8.5 11. 12.5 10. 13. 11. 14.5] Beispiel (Index explizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) print("*** Die gesamte Series ***") print(temp) print("*** Temperatur am Donnerstag ***") print(temp["Do"]) print("*** Tage wärmer als 12 Grad ***") temp_gr_12 = temp[temp > 12] print(temp_gr_12) Ausgabe: *** Die gesamte Series *** Mo 8.5 Di 11.0 Mi 12.5 Do 10.0 Fr 13.0 Sa 11.0 So 14.5 dtype: float64 *** Temperatur am Donnerstag *** 10.0 *** Tage wärmer als 12 Grad *** Mi 12.5 Fr 13.0 So 14.5 dtype: float64 == DataFrame == Ein DataFrame entspricht einer Tabelle. Beispiel (Erzeugen eines DataFrames): import pandas as pd tag = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] v1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=tag) v2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=tag) v3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=tag) tab = pd.concat([v1, v2, v3], axis=1) tab.columns = stadt print(tab) print(type(tab)) Ausgabe: Linz Salzburg Innsbruck Mo 1 8 Hallo Di 2 9 16 Mi 3 10 17 Do 4 11 18 Fr 5 12 19 Sa 6 13 20 So 7 14 21 <class 'pandas.core.frame.DataFrame'> Wir haben hier drei Datenspalten (v1, v2, v3) mit den Spaltennamen "Linz", "Salzburg" und "Innsbruck", sowie sieben Zeilen "Mo" bis "So". Beispiel (Zugriff auf eine Spalte): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab["Salzburg"]) Ausgabe: Mo 8 Di 9 Mi 10 Do 11 Fr 12 Sa 13 So 14 Name: Salzburg, dtype: int64 Beispiel (Zugriff auf eine Zeile): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab.loc["Di"]) Ausgabe: Linz 2 Salzburg 9 Innsbruck 16 Name: Di, dtype: object == Grafiken erstellen == Hier sei nur ein Torten- oder Kreisdiagramm gezeichnet: import pandas as pd import matplotlib.pyplot as plt temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) expl = [0, 0.2, 0, 0, 0, 0, 0] temp.plot.pie(figsize=(5,5), explode=expl) plt.show() Ausgabe: [[Datei:IngPython_pandas1.svg|350px]] Die <code>figsize</code> hat nichts mit der Spaltenanzahl zu tun, sondern setzt die Fenster- oder Diagrammgröße in Inches (Breite * Höhe). Mit Pandas kann man auch noch andere Diagrammarten erstellen, z.B. Linien- oder Balkendiagramme. Dies wird hier aber nicht vorgeführt. == Arbeiten mit CSV- und LibreOffice-Dateien == Gegeben sei eine CSV-Datei test1.csv: Mai;Juni;Juli 12;17;45 15;65;65 16;32;23 4;14;7 23;17;Keine Daten verfügbar 67;Keine Daten verfügbar;45 15;24;12 Lesen von dieser CSV-Datei: import pandas as pd tab = pd.read_csv("test1.csv", sep=";") print(tab) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Schreiben in eine CSV-Datei test2.csv: import pandas as pd val = pd.read_csv("test1.csv", sep=";") val.to_csv("test2.csv", index=False) Ausgabedatei: Mai,Juni,Juli 12,17,45 15,65,65 16,32,23 4,14,7 23,17,Keine Daten verfügbar 67,Keine Daten verfügbar,45 15,24,12 Die Ausgabedatei ist beinahe gleich wie die Eingabedatei. Es werden aber Kommas statt Strichpunkten verwendet. Daher kommt auch der Name CSV ('''C'''omma '''S'''eparated '''V'''alue). Will man wieder Strichpunkte, so kann man dies über den Parameter <code>sep=";"</code> in der <code>to_csv</code>-Funktion machen. Um direkt von einer ODS-Datei (LibreOffice Calc, '''O'''pen '''D'''ocument '''S'''preadsheet) zu lesen muss das Modul odfpy extra installiert sein (per YaST, pip etc.). Gelesen wird dann folgendermaßen: import pandas as pd df = pd.read_excel('test1.ods', engine='odf') print(df) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Die Datei "test1.ods" sei die Datei, aus der das obige "test1.csv" generiert wurde. Schreiben kann man ebenfalls in eine ODS-Datei: import pandas as pd daten = { "Name": ["Hugo", "Richie", "Wilhelmine"], "Alter": [38, 30, 29], "Ort": ["Linz", "Innsbruck", "Salzburg"] } tab = pd.DataFrame(daten) with pd.ExcelWriter("test2.ods", engine="odf") as writer: tab.to_excel(writer, sheet_name="Daten", index=False) Es ergibt sich eine "test2.ods"-Datei (Screenshot): [[Datei:IngPython_pandas2.png|250px]] == Sonstiges == Hier seien stichpunktartig einige weitere Features von Pandas angeführt: * {{W|Pivot-Tabelle|Pivot-Tabellen}} (pivot ... Dreh-, Angelpunkt): Zweck - große Datenmengen schnell zusammenfassen, filtern und analysieren. * NaN ('''n'''ot '''a''' '''n'''umber): In einem Datensatz fehlt ein Wert oder ist nicht definiert. * groupby-Funktion: Daten strukturieren, aggregieren oder Zwischensummen bilden. * {{W|Binning#Datenverarbeitung_und_-analyse|Binning}}: Klassenbildung. * Mehrstufige Indizierung (Hierarchical Indexing oder MultiIndex) * Zeitreihen (Time Series) Für tiefergehende und weiterführende Informationen wird auf das Buch von ''Klein: Numerisches Python; Hanser, 2023'' und [https://pandas.pydata.org/ pandas] verwiesen. = 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) Das funktioniert übrigens nicht nur bei Funktionsannotationen, sondern auch mit Variablen etc. from typing import Final # "Konstante", ab Python 3.8 NUMBER: Final = 123 print(NUMBER) NUMBER = 125 print(NUMBER) # Variable a: int = 345 print(a) a = "Irgendwas" print(a) # Variable (Pipe-Symbol), ab Python 3.10 b : int|str = 567 print(b) b = "sonstiges" print(b) Ausgabe mit python: 123 125 345 Irgendwas 567 sonstiges Ausgabe mit mypy: test3.py:6: error: Cannot assign to final name "NUMBER" [misc] test3.py:12: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] Found 2 errors in 1 file (checked 1 source file) Python kennt übrigens keine Konstanten. Man kann diese mittels Großbuchstaben andeuten. Das ist aber nur Konvention, hat aber keine sonstigen Auswirkungen. Oder man verwendet wie oben gezeigt <code>Final</code>. Dann werfen Typchecker Errors aus. ==pydoc== pydoc ist ein Werkzeug, um Python-Module, -Funktionen und -Klassen zu dokumentieren. Beispiel <code>test1.py</code>: """Rechen-Modul. Funktionen die von pydoc gelesen werden. """ def multiply(a, b): """Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a * b def add(a, b): """Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a + b if __name__ == '__main__': print(multiply(3, 5)) print(add(4, 6)) Ausgabe mit <code>python3.13 -m pydoc test1</code> auf der Konsole: Help on module test1: NAME test1 - Rechen-Modul. DESCRIPTION Funktionen die von pydoc gelesen werden. FUNCTIONS add(a, b) Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl multiply(a, b) Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl FILE /home/xyz/tmp/test1.py Siehe zu diesem Thema z.B. auch [https://docs.python.org/3/library/pydoc.html]. Mittels <code>python3.13 -m pydoc -w test1</code> kann man auch eine HTML-Dokumentation erstellen. Wofür <code>if __name__ == '__main__':</code> steht, siehe z.B. etwas später im Abschnitt [[Ing_Mathematik:_Python#Doctests]]. == 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.3 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.5.1 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://build123d.readthedocs.io/en/latest/ build123d] * [https://docs.vtk.org/en/latest/api/python.html VTK] * [https://docs.bokeh.org/en/latest/docs/first_steps.html bokeh] * [https://py5coding.org/ py5] == 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] * [https://pint.readthedocs.io/en/stable/ pint] = 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] * Iacono et al.: Programmieren trainieren; 3. Aufl., Hanser, 2023, ISBN 978-3-446-47766-7 * 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}} 70n259gbr79ctiqbiadsbnygj4dqgsn 1092070 1092069 2026-10-11T11:12:20Z Intruder 1513 /* Rekursive Funktionen */ 1092070 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 (welche Verzeichnisse im PATH liegen, kann bspw. mit <code>echo $PATH</code> abgefragt werden). 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 == === Tests === 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) === Popularität === * An Beliebtheit rangiert Python mit Stand September 2026 mit einem Rating von 17,76% 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 Q1/2026 auf Rang 2, vor TypeScript und hinter JavaScript. * [https://pypl.github.io/PYPL.html PYPL] (Popularity of Programming Language Index) ist ein anderer Popularitätsindex für Programmiersprachen. Auch hier liegt Python mit großem Vorsprung auf dem ersten Platz (52,08%), gefolgt von Java (13,34%) und C/C++ (7,99%) (Stand September 2026). === Namensgebung === 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 in PyConUS24.jpg|{{W|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 <code>help</code> oder <code>help()</code> 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, bzw. der Eingabe von q, quit oder exit mit anschließendem Drücken der RETURN-Taste. == 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 Theoretisch kann man auch Umlaute, griechische Buchstaben u.ä. in Variablenbezeichnern nutzen, z.B. ößÜ = 16 α = 134 print(ößÜ) print(α) Ausgabe: 16 134 Davon wird aber aus offensichtlichen Gründen ({{W|Englisch als Lingua franca}}) abgeraten. Das Eurozeichen (€) etc. darf man ohnehin nicht in Variablenbezeichnern verwenden, dies ergäbe einen Syntaxfehler. Siehe dazu auch [https://peps.python.org/pep-3131/]. 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")) # new style string formatting print("Hallo {:s}" . format("Hugo")) # new style string formatting print("Hallo %s" % "Hugo") # %-String 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 f-Strings (ab Python 3.6) sind die modernere Variante gegenüber %-Strings oder das ''new style string formatting''. Sie sind in der Regel auch schneller. 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. Man beachte, dass {{W|Rundungsfehler}} auftreten können. D.h. man sollte bei der Verwendung von {{W|Gleitkommazahl|Gleitpunktzahlen}} niemals auf absolute Genauigkeit prüfen, sondern eine Toleranz (einen <math>\varepsilon</math>-Wert) vorgeben. Ein klassisches Beispiel, das ein falsches Ergebnis liefert, ist: i = 0.1 + 0.2 print(i) if i == 0.3: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 NOK Und das, obwohl man schon in der Volksschule lernt, dass 0.1 + 0.2 gleich 0.3 ist. Korrekt muss das obige Beispiel z.B. so umgeschrieben werden: ist = 0.1 + 0.2 soll = 0.3 epsilon = 0.000001 print(ist) if abs(ist-soll) < epsilon: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 OK Alternativ kann auch die <code>math.isclose()</code>-Funktion verwendet werden. Das sei aber hier nur erwähnt und nicht konkret ausgeführt. <u>Aufgabe:</u> Recherchieren Sie im Internet zum Thema <code>math.isclose()</code>-Funktion. === 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 == Kopfgesteuerte Schleife, Endlosschleife und fußgesteuerte Schleife: [[File:Programmingloops.svg|450px]] === 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 Der <code>break</code>-Befehl wird etwas später noch einmal kurz thematisiert. === 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!") print("Setze Programm fort!") Ausgabe: Das Ergebnis ist unendlich Setze Programm fort! 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). Danach wird das Programm fortgesetzt. 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. Neu in Python 3.14 ist, dass mehrere Ausnahmen auch ohne Klammern angegeben werden können, z.B.: bis inkl. Python 3.13 nur: except (ZeroDivisionError, ValueError): ab Python 3.14 auch: except ZeroDivisionError, ValueError: == 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 c, d = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(c) print(d) Ausgabe: 8 15 Es werden r1 und r2 zurückgegeben und den Variablen c und d zugewiesen. (r1, r2) und (c, d) sind Tupel. Was Tupel konkret sind, siehe etwas später in diesem Kapitel. 3 und 5 sind die aktuellen Parameter (die Argumente). a und b sind die formalen Parameter. Die Zuweisung der aktuellen zu den formalen Parametern erfolgt in der gegebenen Reihenfolge, also die Zahl 3 wird an a übergeben und die Zahl 5 an b. Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 c, d = mathFunc(3, 5) print(c) print(d) e, f = mathFunc(5) print(e) print(f) g, h = mathFunc(b=6) print(g) print(h) Ausgabe: 8 15 25 100 16 60 Erklärung: * Erster <code>mathFunc</code>-Aufruf: Es werden, wie weiter oben, die Argumente 3 und 5 an die formalen Parameter a und b übergeben. Die Rückgabewerte r1 und r2 werden an c und d übergeben. Somit ist c=8 und d=15. * Zweiter <code>mathFunc</code>-Aufruf: Es wird nur das Argment 5 an a übergeben. b behält den Vorgabewert 20. D.h. e= 25 und f=100. * Dritter <code>mathFunc</code>-Aufruf: Es wird nur die Zahl 6 an b übergeben, a behält seinen Vorgabewert 10. Somit ergibt sich g=16 und h=60. Beispiel (Schlüsselwortargumente vs. Positionsargumente): def mathFunc(a, b): r1 = a - b r2 = a / b return r1, r2 c, d = mathFunc(b=3, a=5) # Schlüsselwortargumente e, f = mathFunc(3, 5) # Positionsargumente print(c) print(d) print(e) print(f) Ausgabe: 2 1.6666666666666667 -2 0.6 Durch die Verwendung von Schlüsselwortargumenten können die Argumente in beliebiger Reihenfolge angegeben werden. Bei den Positionsargumenten ist die Reihenfolge vorgegeben. Daraus resultiert auch der Unterschied bei den obigen Ausgaben. Werden beide Arten in einem Aufruf gemischt, so müssen die Positionsargumente zuerst angegeben werden. Den Unterschied zwischen beiden Argumentarten zu kennen ist wichtig, nicht nur bei selbst erstellten Funktionen. Auch beim Aufruf von Python-Funktionen verfolgt einen die Mischung von Schlüsselwort- und Positionsargumenten auf Schritt und Tritt, z.B. beim etwas später folgenden Abschnitt über "Grafiken zeichnen": plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") Bei dieser <code>plot</code>-Funktion aus dem Modul <code>matplotlib.pyplot</code> sind <code>x</code> und <code>y1</code> Positionsargumente. <code>label, lw</code> und <code>ls</code> sind die Namen der Schlüsselwortargumente. Die Schlüsselwortargumente könnten auch in anderer Reihenfolge angeschrieben werden, nicht jedoch die Positionsargumente! Siehe zu <code>matplotlib.pyplot.plot</code> auch die Seite [https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html]. == 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 {{W|Rekursion|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 Typische Beispiele für die Abarbeitung als rekusive Funktionen sind die {{W|Fibonacci-Folge}} und die {{W|Fakultät_(Mathematik)|Fakultät}}. Jedoch ist der iterative Ansatz (mit konventionellen Schleifen) meist schneller und speicherplatzschonender. == 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 und Listen == 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. 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. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. # 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 (eine Liste von Wörtern zu einem einzigen zusammenhängenden Text verbinden): woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt == Dictionaries und Mengen == 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'> Leere Mengen lassen sich mit <code>set()</code> erzeugen: menge = set() # leere Menge print(menge) menge.add("Hallo") # Element hinzufügen print(menge) Ausgabe: set() {'Hallo'} Beispiel (einige Mengenoperationen): mengeA = {"Hallo", "Welt"} mengeB = {1, 2, "Welt", 3} # Vereinigung print(mengeA | mengeB) print(mengeA.union(mengeB)) # Durchschnitt print(mengeA & mengeB) print(mengeA.intersection(mengeB)) # Differenz print(mengeA - mengeB) print(mengeA.difference(mengeB)) # Symmetrische Differenz print(mengeA ^ mengeB) print(mengeA.symmetric_difference(mengeB)) # Teilmenge print(mengeA.issubset(mengeB)) # Mehrere Elemente hinzu mengeA.update(["Super", 67, 125]) print(mengeA) # Element löschen mengeA.discard("Hallo") print(mengeA) Ausgabe: {1, 2, 3, 'Welt', 'Hallo'} {1, 2, 3, 'Welt', 'Hallo'} {'Welt'} {'Welt'} {'Hallo'} {'Hallo'} {1, 2, 3, 'Hallo'} {1, 2, 3, 'Hallo'} False {67, 'Super', 'Welt', 'Hallo', 125} {67, 'Super', 'Welt', 125} Es gibt schon seit "ewigen" Zeiten unveränderbare Mengen, die mittels <code>frozenset</code> eingeleitet werden. Neu ab Python 3.15 ist dagegen das Pendant <code>frozendict</code>, das unveränderbare Dictionaries darstellt. fdict = frozendict({"vorname":"Hugo", "nachname":"Meister"}) fset = frozenset({"A", 1, "A"}) print(fdict) print(fdict["vorname"]) print (fset) try: fdict["vorname"] = "Max" print(fdict["vorname"]) except TypeError as e: print(f"Fehler: {e}") try: fset.add("B") print(fset) except AttributeError as e: print(f"Fehler: {e}") Ausgabe: frozendict({'vorname': 'Hugo', 'nachname': 'Meister'}) Hugo frozenset({1, 'A'}) Fehler: 'frozendict' object does not support item assignment Fehler: 'frozenset' object has no attribute 'add' == 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: x = ("a", "b", "c", "d", "e") print(x[2]) print(x[-3]) print(x) print(x[:]) print(x[2:]) print(x[:2]) print(x[::-1]) Ausgabe: c c ('a', 'b', 'c', 'd', 'e') ('a', 'b', 'c', 'd', 'e') ('c', 'd', 'e') ('a', 'b') ('e', 'd', 'c', 'b', 'a') Visualisierung: '''a''' '''b''' '''c''' '''d''' '''e''' +-----------------------------+ | 0 | 1 |//'''2'''//| 3 | 4 | +-----------------------------+ | -5 | -4 |/'''-3'''//| -2 | -1 | +-----------------------------+ |/////////////////////////////| +-----------------------------+ |/////////////''':'''///////////////| +-----------------------------+ | | |///////'''2:'''////////| +-----------------------------+ |////''':2'''/////| | | | +-----------------------------+ |\\\\\<--\\\\\'''::-1'''\\\\<--\\\\\| +-----------------------------+ Beispiel: x = ("a", "b", "c", "d", "e") print(x[2::2]) print(x[2::-1]) print(x[2:0:-1]) Ausgabe: ('c', 'e') ('c', 'b', 'a') ('c', 'b') Hier sieht man sehr schön, dass beim letzen <code>print</code>-Befehl die 0, die in diesem Fall den Buchstaben "a" repräsentiert, als letzer Wert nicht mehr dazugehört (wie weiter oben angedeutet ist der End- oder Stoppwert exklusiv). Will man den Buchstaben "a" dazunehmen, so ist die vorletzte Zeile die richtige. Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH Mit zweidimensionalen Listen/Tupels wird das Ganze schon bedeutend komplexer. Beispiel: Verkettetes Slicing A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] print(A[2::][::2]) Ausgabe: [['20', '21', '22', '23'], ['40', '41', '42', '43']] Das erste Slicing erstellt folgenden Slice: [["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] Das zweite Slicing wird dann auf diesen Slice angewendet und ergibt das genannte Endergebnis. Will man Spalten statt Zeilen, so funktioniert dieser direkte Weg nicht. Man verwendet dann List Comprehensions oder die NumPy-Bibliothek. Diese Bibliothek erlaubt zweidimensionales Slicing. Sie wird erst später bei den Grafiken eingeführt. Deshalb wird hier dazu noch kein Beispiel gezeigt. = 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 (ist der sogenannte Konstruktor). 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 strikt/stark private ist. Wird nur ein Unterstrich verwendet, so deutet dies auf schwach/weakly private hin. Letzteres wird in Python deutlich häufiger verwendet. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. Besonderheiten bei stark und schwach private: * '''stark private''': ** zwei führende Unterstriche ** Name Mangling: Name wird zu <code>_Klassenname__variablenname</code> ** Schutz vor Namenskollisionen bei Vererbung ** am Ende des Namens dürfen keine zwei Unterstriche verwendet werden. Das wären dann Magic Methods oder Dunder-Methods (z.B. __init__). Diese sind public. * '''schwach private''': ** ein führender Unterstrich ** Name bleibt gleich ** ist nur ein Hinweis an die Programmierer, kein starker Schutz ** <code>from modul import *</code>: Objekte mit einem führenden Unterstrich werden standardmäßig nicht importiert ** Standard für die Kapselung bei Python. Schätzung: Weit mehr als 80% der nicht-öffentlichen Attribute/Methoden werden als schwach private deklariert. Weniger als 5-10% als stark private (Quelle: Google KI) == 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 == === Einfachvererbung === [[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 === Mehrfachvererbung === Mit Python ist auch Mehrfachvererbung möglich, z.B.: class A: def _printA(self): print("Klasse A") class B: def _printB(self): print("Klasse B") class C(A,B): def __init__(self): self._printA() self._printB() c = C() Ausgabe: Klasse A Klasse B Die Klasse C erbt von A und B. In UML sieht das in etwa so aus: +-----------------------------+ +-------------------------+ | A | | B | +-----------------------------+ +-------------------------+ | | | | +-----------------------------+ +-------------------------+ | # printA() | | # printB() | +-----------------------------+ +-------------------------+ . . / \ / \ --- --- | | | | +---------------------+ | C | +---------------------+ | | +---------------------+ | | +---------------------+ <code>printA()</code> und <code>printB()</code> sind schwach private (in UML als <code>protected</code> mit einem # dargestellt). Es ist aber zu beachten, dass schwach private nur Konvention ist. Praktisch könnte man auch von außerhalb auf diese Methoden zugreifen. Das soll man aber nicht tun, und das wird in Python durch einen Unterstrich angedeutet. == Statische Attribute, statische Methoden und Klassenmethoden == Beispiel: class Math: pi = 3.14159265 cnt = 0 @staticmethod def expo(): return 2.71828183 @classmethod def counterPlus(cls): cls.cnt += 1 print(Math.pi) print(Math.expo()) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Ausgabe: 3.14159265 2.71828183 1 2 3 Statische Attribute und Methoden, bzw. Klassenmethoden, sind direkt einer Klasse zugeordnet und nicht einem Objekt. Der Unterschied zwischen statischen Methoden und Klassenmethoden ist, dass statische Methoden nicht auf Klassenattribute (bzw. statische Attribute) zugreifen können, Klassenmethoden aber schon. <code>pi</code> und <code>cnt</code> sind statische Attribute. <code>expo()</code> ist eine statische Methode. <code>counterPlus(cls)</code> ist eine Klassenmethode. Die Bezeichnung des Parameters als <code>cls</code> ist reine Konvention, man sollte sich aber daran halten. = 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. Die "arange"-Funktion aus der NumPy-Bibliothek wird später ein bisschen genauer behandelt (siehe [[Ing_Mathematik:_Python#Zahlenfolgen|Zahlenfolgen]]). <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. Die Farbe des Graphen kann man über den <code>color</code>-Schalter einstellen, z.B. auf rot setzen: plt.plot(x, y, color="red") Das ist nur eine Möglichkeit, es gibt auch andere, z.B. mittels * hexadezimaler Zahl, z.B. <code>color="#ff0000"</code> * Kurzform, z.B. <code>"r"</code> Statt <code>color</code> kann auch die Kurzform <code>c</code> verwendet werden. <u>Kleine Aufgabe:</u> Probieren Sie diese "Farbspielereien" am Computer aus. === Graphen mehrerer Funktionen und weiteres === ==== Mehrere Funktionen in einen Plot zeichnen ==== 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]] Einige weitere Bespiele zu <code>ls</code></code>: * - ... durchgezogene Linie * -. ... Strichpunkt-Linie * dashed ... gestrichelte Linie ==== Marker ==== Desweiteren kann man Marker verwenden. Beispiel: import matplotlib.pyplot as plt plt.plot(10, 10, "ob", markersize=30) plt.plot(20,20, ".r") plt.plot(30,30, "Dg", ms=15) plt.plot(40, 40, "ok", ms=20, mec="#FF5733", mew=3) plt.plot(50, 50, "vk", ms=20, mec="yellow", mew=3) plt.grid() plt.show() Das erste Zeichen im String nach den Koordinaten im plot-Befehl symbolisiert die Form (z.B. o für einen Kreis). Das zweite Zeichen steht für die Farbe (z.B. b für blue). <code>markersize</code> oder <code>ms</code> setzt die Größe des Markers. Ausgabe: [[Bild:PythonIng_formatpara1.svg|200px]] Einige weitere Marker: * * ... Stern * + ... Plus * h oder H ... Sechseck Man kann mit den Parametern * <code>markeredgecolor</code> oder kurz <code>mec</code> die Farbe des Markerrandes * <code>markeredgewidth</code> oder kurz <code>mew</code> die Dicke des Randes * <code>markerfacecolor</code> oder kurz <code>mfc</code> die Innenfarbe des Markers bestimmen. === 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]] === Anmerkungen hinzufügen === Anmerkungen (engl. annotations) lassen sich mit matplotlib einfach hinzufügen. Unter Anmerkung wird hier ein Pfeil mit Text verstanden. Beispiel: import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot([1,10], [1,10]) ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0)) plt.show() Ausgabe: [[Datei: PythonIng_ann1.svg|400px]] <code>shrink</code> verkürzt den Pfeil. Sinnvoll sind beim obigen Beispiel Werte irgendwo zwischen 0.0 und 0.3. <u>Kleine Aufgabe:</u> Probieren Sie verschiedene Pfeillängen aus. Die Schriftgröße verändern können Sie übrigens mit <code>fontsize</code> oder <code>size</code>, z.B. ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0), fontsize=25) Eine andere Schreibweise für Dictionaries, die wir weiter oben schon kennengelernt haben, die wir aber nochmals explizit aufschreiben wollen, ist ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops={"facecolor":"red", "shrink":0.0}) === Figure, Axes etc.=== [[Bild:PythonIng_figure33.svg|350px]] * fig (figure) ... das übergeordnete Fenster * ax (axes) ... "Plot", das eigentliche Zeichenfenster * axis ... die Koordinatenachsen * spine ... Begrenzungslinien des Zeichenfensters * tick ... Markierung an den Achsen des Zeichenfensters Beispiel (eine figure mit 6 axes, ax-Titel, y-axis-Titel): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=3, ncols=2) ax[0][0].text(0.1, 0.5, "11") ax[0][1].text(0.1, 0.5, "12") ax[1][0].text(0.1, 0.5, "21") ax[1][1].text(0.1, 0.5, "22") ax[2][0].text(0.1, 0.5, "31") ax[2][1].text(0.1, 0.5, "32") ax[0][0].set(ylabel="y-label", title="axes 1") plt.show() [[Bild:PythonIng_figure34.png|350px]] Beispiel (Achsenbereiche setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-1, 10, 1, 12)) plt.show() [[Bild:PythonIng_figure35.svg|350px]] Beispiel (Gitter einschalten): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-5, 10, 1, 12)) ax[0].minorticks_on() ax[0].grid(which='minor') ax[1].grid(which='major') plt.show() [[Bild:PythonIng_figure36.svg|350px]] Beispiel (ticks setzen) import matplotlib.pyplot as plt import matplotlib.ticker as tick fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") pos = [0, .2, .5] lab = ["a", "b", "c"] ax.xaxis.set_major_locator(tick.FixedLocator(pos)) ax.xaxis.set_major_formatter(tick.FixedFormatter(lab)) plt.show() [[Bild:PythonIng_figure37.svg|350px]] Beispiel (Achsenkreuz beliebig setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") ax.spines['top'].set_color('none') ax.spines['right'].set_color('none') ax.spines['bottom'].set_position(('data', 0.3)) ax.spines['left'].set_position(('data', 0.5)) plt.show() [[Bild:PythonIng_figure38.svg|350px]] Beispiel (Inset): import matplotlib.pyplot as plt # Hauptplot erstellen fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") # Inset hinzu: axin = ax.inset_axes([0.4, 0.4, 0.3, 0.3]) axin.text(0, 0, "Servus") axin.plot() plt.show() [[Bild:PythonIng_figure40.svg|350px]] Beispiel (Hintergrundfarben setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.set_facecolor("green") fig.set_facecolor("yellow") plt.plot([1, 3], [6, 8]) plt.show() [[Bild:PythonIng_figure39.svg|300px]] === GridSpec === Mit figures und axes lassen sich schon jede Menge an Grafikdetails einstellen. GridSpec ist dann der Turbo oder Raketenantrieb den matplotlib eingebaut hat. Naja, vielleicht ist das etwas übertrieben, aber GridSpec ist sozusagen ein flexibler Layout-Manager. Bisher haben wir immer gleich große axes gezeichnet. Mit GridSpec können wir Subplots über mehrere Zeilen und Spalten ziehen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) gs = fig.add_gridspec(3, 3) ax1 = fig.add_subplot(gs[0, :]) ax1.set_title("Bereich 1: gs[0, :]") ax2 = fig.add_subplot(gs[1:, 0]) ax2.set_title("Bereich 2: gs[1:, 0]") ax3 = fig.add_subplot(gs[1, 1:]) ax3.set_title("Bereich 3: gs[1, 1:]") ax4 = fig.add_subplot(gs[2, 1:]) ax4.set_title("Bereich 4: gs[2, 1:]") plt.show() [[Bild:PythonIng_gridspec1.svg|350px]] Erklärung zu <code>(gs[zeile, spalte])</code>: Slicing; damit legen Sie fest, welche Zellen ein Subplot belegt. Achtung! Die Indizes beginnen bei 0 und nicht bei 1 (d.h., die erste Zeile/Spalte hat den Index 0) * : ... alle * 1: ... 1 bis Ende * etc. <code>constrained_layout=True</code> sorgt dafür, dass sich die Subplots nicht überlappen. Aber auch die Spaltenbreiten und Zeilenhöhen lassen sich anpassen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) # Verhältnis erste zu zweite Zeile = 1.5:1 # Verhältnis erste zu zweite Spalte = 1:4 gs = fig.add_gridspec(2, 2, figure=fig, height_ratios=[1.5, 1], width_ratios=[1, 4]) ax1 = fig.add_subplot(gs[0, 0]) ax1.set_title("Bereich 1: gs[0, 0]") ax2 = fig.add_subplot(gs[0, 1]) ax2.set_title("Bereich 2: gs[0, 0]") ax3 = fig.add_subplot(gs[1, 0]) ax3.set_title("Bereich 3: gs[1, 0]") ax4 = fig.add_subplot(gs[1, 1:]) ax4.set_title("Bereich 4: gs[1, 1]") plt.show() [[Bild:PythonIng_gridspec2.svg|350px]] Auch die Ränder lassen sich einstellen. Beispiel 1: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.2, hspace=0.2) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec3.svg|300px]] * hspace ... height space, vertikaler Abstand zwischen den Zeilen * wspace ... width space, horizontaler Abstand zwischen den Spalten Und im Vergleich dazu Beispiel 2: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.3, right=0.7, bottom=0.3, top=0.7, wspace=0.3, hspace=0.3) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec4.svg|300px]] Es gibt noch einige weitere Details, die sich mit GridSpec variieren lassen (z.B. SubGridSpec ... verschachtelte Grid-Layouts). Aber wir belassen es vorerst dabei. Das bisher Gezeigte sollte für die meisten Probleme ausreichend sein. === 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|300px|center]] * 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|300px|center]] * 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. * Zeichnen Sie folgendes Bild (die Achsenbeschriftungen positionieren Sie z.B. mit den Befehlen <code>ax.set_xlabel("x", loc="right"); ax.set_ylabel("y", loc="top")</code>): [[Bild:PythonIng_aufgabe24.svg|400px|center]] * Zeichnen Sie mit Python, matplotlib und NumPy folgendes Bild: [[Bild:PythonIng_figure41.svg|400px|center]] * Zeichnen Sie mit GridSpec ein Layout, das in etwa wie folgendes aussieht (<code>height_ratios=[1, 2], width_ratios=[2, 1]</code>): [[Bild:PythonIng_aufgabe23.svg|300px|center]] == 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 und fortgeschrittenes 3D == === 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 build123d === Hiermit lassen sich einfach 3D-Modelle für CAD-Systeme erstellen. [https://build123d.readthedocs.io/en/latest/ build123d] ist ein externes Modul und muss vorab, wie schon für mehrere andere Module gezeigt, installiert werden (pip, venv etc.). Beispielcode: from build123d import * # Bauteil with BuildPart() as bauteil: # Zylinder erstellen Cylinder(100, 60) # Bohrung with BuildSketch(bauteil.faces().sort_by(Axis.Z)[-1]) as loch: Circle(radius=10) # negativ extrudieren extrude(amount=-60, mode=Mode.SUBTRACT) # Dem Bauteil z.B. die Farbe rot zuweisen # bauteil.part.color = Color("red", alpha=1.0) # Fertiges Modell exportieren export_step(bauteil.part, "bauteil.step") Ausgabe ({{W|Standard for the exchange of product model data | STEP}}-Datei, z.B. in {{W|FreeCAD}} importiert, Screenshot): [[Datei:PythonIng_build123d.jpg | 300px]] FreeCAD ist ein freies 3D-CAD-System. Die [[Ing_Mathematik:_Python#FreeCAD | Makroprogrammierung]] in diesem System wird später noch gezeigt. STEP steht für "'''St'''andard for the '''e'''xchange of '''p'''roduct model data" und ist ein Datenaustauschformat. === 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> == Sonstige Grafikbibliotheken == === Py5 === Im Buch ''Iacono et al.: Programmieren trainieren, mit über 150 Workouts in Java und Python; 3. Aufl., Hanser, 2023'' werden zahlreiche Aufgaben gestellt, die u.a. mit Processing zu lösen sind. Processing ist eine einfache Programmiersprache und Entwicklungsumgebung. Warum ich das erwähne? Weil in älteren Processing-Versionen auch die Programmiersprache Python 2 abgedeckt war. Bei neueren Versionen gibt es keine Python-Anbindung mehr (getestet nur mit openSUSE Tumbleweed und Python 3). Somit funktioniert der Python-Teil im genannten Buch zum Teil nicht mehr. Ist das tragisch, oder kann das Buch weggeschmissen werden? Nein! Es gibt Alternativen zu Processing. Zum einen die im Buch selbst genannten Bibliotheken tkinter oder pygame. Pygame wird dort auch kurz anhand eines Beispiels thematisiert. Aber noch besser ist es, die py5-Bibliothek zu verwenden (lt. Google KI vom 30. Sept. 2026: ''Der echte, moderne „spirituelle Nachfolger“''). Damit ist die Funktionalität von Processing gewährleistet. Man kann aber beliebige Editoren oder IDEs zur Programmerstellung verwenden, ohne an Processing als IDE gebunden zu sein. Nachfolgend werden ein paar Beispiele zu dieser externen Bibliothek gegeben. Der erste Schritt ist wieder, die py5-Bibliothek zu installieren (z.B. mittels pip in eine virtuelle Umgebung; Python>=3.10 und ein JDK>=17 müssen auch installiert sein (Stand Okt. 2026)). Dann kann es schon mit dem Programmieren losgehen. Beispiel (angelehnt an die ''Aufgabe W.2.3 Haus'' des genannten Buchs, jedoch etwas erweitert): import py5 def setup(): py5.size(600, 600) # Fenstergröße 600px x 600px py5.background(255, 255, 255) # Hintergrundfarbe ... weiss def draw(): # Haus zeichnen py5.fill(255, 255, 255) # Hintergrundfarbe ... weiss py5.stroke(255, 0, 0) # Linienfarbe ... rot py5.stroke_weight(3) # Linienbreite ... 3px py5.rect(150, 200, 300, 300) # Rechteck zeichnen (x, y, b, h) py5.triangle(150, 200, 300, 100, 450, 200) # Dreieck zeichnen py5.circle(250, 300, 70) # Kreis zeichnen (x, y, d) # Bemassung zeichnen py5.stroke(0, 255, 0) py5.stroke_weight(1) py5.line(300, 100, 550, 100) # Linie zeichnen py5.line(450, 200, 550, 200) py5.line(450, 500, 550, 500) py5.line(150, 500, 150, 550) py5.line(450, 500, 450, 550) py5.text_size(20) # Textgroesse py5.fill(0, 255, 0) # Textfarbe py5.text("300", 280, 550) # Text zeichnen py5.text("100", 500, 150) py5.text("300", 500, 350) # Gedrehtes Rechteck zeichnen py5.stroke(255, 255, 0) py5.stroke_weight(3) py5.fill(200, 200, 200) py5.translate(320, 260) py5.rotate(0.3) # Drehung (in Radiant) py5.rect(0, 0, 100, 50) # Startet den Sketch py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_1.png|300px]] Das skizzierte Haus ist mit einer rudimentären Bemaßung versehen und hat zwei Fenster, ein rundes und ein gedrehtes, trübes rechteckiges. Beispiel (Mausoperationen): import py5 def setup(): py5.size(600, 600) py5.background(255, 255, 255) def draw(): pass def mouse_pressed(): py5.text_size(15) py5.fill(0, 0, 255) x = py5.mouse_x y = py5.mouse_y py5.text("(%d, %d)" %(x, y), x, y) # Positionsdaten bei Maus-Klick schreiben py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_2.png|300px]] Obiges Bild zeigt das Resultat nach einigen Mausklicks. Man erkennt, dass links-oben der Koordinatennullpunkt ist. Die x-Achse zeigt nach rechts, die y-Achse nach unten. Beispiel (Fünfeck): import py5 import random def setup(): py5.size(400, 400) py5.no_loop() # statisches Bild (schont die Ressourcen) def draw(): py5.translate(py5.width / 2, py5.height / 2) # KS setzen radius = 150 eck_anzahl = 5 py5.fill(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # Füllfarbe auf Zufallswert setzen py5.stroke(155, 0, 0) py5.stroke_weight(5) # Fünfeck als Polygon zeichnen py5.begin_shape() for i in range(eck_anzahl): winkel = i * py5.TWO_PI / eck_anzahl - py5.HALF_PI x = py5.cos(winkel) * radius y = py5.sin(winkel) * radius py5.vertex(x, y) py5.end_shape(py5.CLOSE) py5.run_sketch() Ausgabe, bspw.: [[Datei:PythonIng_py5_5.png|200px]] <u>Kleine Aufgaben:</u> * Um die Funktionsweise von <code>py5.no_loop()</code> zu verstehen, kommentieren Sie die Anweisung im Beispiel einfach aus, und starten Sie das Programm erneut. * Zeichnen Sie ein Siebeneck. * Was passiert, wenn man <code>py5.CLOSE</code> im <code>py5.end_shape()</code>-Befehl weglässt? Beispiel (Grafik laden und zeichnen): import py5 image1 = None def setup(): global image1 py5.size(600, 600) py5.no_loop() image1 = py5.load_image("PythonIng_py5_1.png") def draw(): py5.image(image1, 0, 0) py5.run_sketch() Hier wird einfach das Bild vom Haus-Beispiel (Screenshot von weiter oben) geladen und gezeichnet. Beispiel (angelehnt an ''W.5.10 Wurfparabel'' des genannten Buches; mit OOP erstellt): import py5 class Wurfparabel(py5.Sketch): def __init__(self, v0, angle): super().__init__() self.v0 = v0 self.angle = self.radians(angle) self.g = 9.81 self.t = 0.0 self.scale = 1.5 def settings(self): self.size(800, 600) def setup(self): self.background(0, 0, 0) def draw(self): self.t += 0.1 self.translate(50, 500) # KS Zeichnen self.fill(255, 255, 0) self.stroke(255, 255, 0) self.stroke_weight(1) self.line(0, 0, 0, -300) self.line(0, 0, 700, 0) # Punkt zeichnen cx = self.v0 * self.cos(self.angle) * self.t * self.scale cy = -(self.v0 * self.sin(self.angle) * self.t - \ self.g/2 * self.t**2) * self.scale self.fill(255, 0, 0) self.stroke(255, 0, 0) self.circle(cx, cy, 3) # Stoppen bei Bodenkontakt if cy >= -5: self.no_loop() def wurfparabel(v0=50, angle=45): sketch = Wurfparabel(v0, angle) sketch.run_sketch() v0 = float(input("v0 = ")) winkel = float(input("winkel = ")) wurfparabel(v0=v0, angle=winkel) Eingabe auf der Konsole: v0 = 60 winkel = 45 Ausgabe: [[Datei:PythonIng_py5_13.png|200px]] Die benötigten Formeln der Bewegungsgleichung kann man leicht herleiten, bzw. man benutzt folgende Grafik als Hilfestellung: [[Datei:Wurfparabel_Zusammenfassung_aktualisierung.png|700px]] <u>Aufgabe:</u> Berechnen Sie den Scheitelpunkt und die Wurfweite bei gegebener Anfangsgeschwindigkeit und gegebenem Abwurfwinkel. Die Abwurfhöhe <math>y_0</math> sei 0. Der Wurf soll auf der Erde stattfinden (g = 9.81 m/s²). Die dazu benötigten Formeln können aus obiger Grafik entnommen werden (oder Sie leiten sie selbst her). Geben Sie die Ergebnisse auf der Konsole aus. Beispiel (eine einfache Animation): import py5 i = 0 def setup(): py5.size(600, 600) py5.frame_rate(5) py5.stroke_weight(20) def draw(): global i py5.background(255, 255, 255) py5.translate(py5.width/2, py5.height/2) py5.rotate(py5.PI/8 * i) py5.line(-100, 0, 100, 0) i += 1 py5.run_sketch() Es wird eine rotierende Linie gezeichnet (z.B. wie bei einem Installationsprozess verwendet - zeigt an, dass der Installationsfortschritt nicht eingefroren ist). Hierbei ist nur aufzupassen, dass der Hintergrund in <code>draw()</code> gesetzt wird, ansonsten würden die Linien additiv gezeichnet. Das ist zwar auch schön, aber eben nicht das gewünschte Resultat. Hiermit belassen wir es mit der py5-Bibliothek und wenden uns wieder "mathematischeren" Themen zu. = 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> standardmäßig aber sehr wohl. Jedoch kann man das bei <code>linspace</code> auch ändern. Dann ist der Stoppwert nicht mehr enthalten: linspace(start, stop, num, endpoint=False) == 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 === Beispiel: 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 Visualisierung: 0 1 2 +-----------+ 0 | 1 | 2 | 3 | +-----------+ 1 | 4 | 5 | '''6''' | +-----------+ Beispiel: import numpy as np A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] B = np.matrix(A) print(B[2::, ::2]) Ausgabe: [['20' '22'] ['30' '32'] ['40' '42']] Mit der NumPy-Bibliothek funktioniert das zweidimensionale Slicing. Mit normalen Listen/Tupels geht das nicht, was wir schon früher thematisiert haben. === 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]. Beispiel: import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: <Compressed Sparse Row sparse array of dtype 'float64' with 5 stored elements and shape (5, 5)> Coords Values (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 csr steht für '''c'''ompressed '''s'''parse '''r'''ow. Andere Möglichkeiten: * coo-Format ('''coo'''rdinate Format): <code>scipy.sparse.coo_array()</code> * lil-Format ('''li'''st of '''l'''ists): <code>scipy.sparse.lil_array()</code> * csc-Format ('''c'''ompressed '''s'''parse '''c'''olumn): <code>scipy.sparse.csc_array()</code> Es gibt auch <code>scipy.sparse.csr_matrix()</code> etc. Diese Befehle sind aber veraltet und es wird empfohlen, statt dessen die <code>..._array()</code>-Funktionen zu verwenden. <u>Aufgaben:</u> Recherchieren Sie im Internet * generell zum Thema "spärlich besetzte Matrizen mit Python". * Was sind die Vor-/Nachteile von csr-arrays gegenüber coo- und lil-arrays? * Wann verwendet man sinnvollerweise csr- und wann csc-arrays? * Wie wandelt man coo-arrays in csr-arrays um? = 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.] Siehe dazu auch das Kapitel [[Ing Mathematik: Numerisches Lösen von linearen Gleichungssystemen]]. Dort wird dieses Themengebiet u.a. mit weitergehenden Python-Skripten vertieft. == 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") <u>Aufgaben</u>: Ermitteln Sie mittels SymPy die <math>\mathcal{L}</math>-Transformierten und dann wieder die <math>\mathcal{L}^{-1}</math>-Transformationen (ähnlich wie beim obigen Beispiel): * <math>\sin(t) \circ\! -\! \bullet \ ?</math> * <math>t^3 \circ\! -\! \bullet \ ?</math> * <math>\text{e}^{\omega t} \circ\! -\! \bullet \ ?</math> =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 [https://mpmath.org/ 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). = pint - mit physikalischen Größen rechnen = [[File:Pint-Glas_UK.jpg|right|100px]] pint ist ein externes Python-Paket zur Manipulation von physikalischen Einheiten. Es muss vor der ersten Benutzung installiert werden (Vorgehensweise wie immer: pip, YaST, via venv etc.). Beispiel: import pint import numpy as np ureg = pint.UnitRegistry() # Einfache Berechnung print(3 * ureg.meter + 4 * ureg.cm) # Mit NumPy npeinzel = [3, 4] * ureg.meter + [2, 5] * ureg.cm print(npeinzel) npsum = np.sum(npeinzel) print(npsum) # Konvertierung von Einheiten npsum_inch = npsum.to(ureg.inch) print(npsum_inch) print(npeinzel.magnitude) print(npeinzel.units) print(npeinzel.dimensionality) Ausgabe: 3.04 meter [3.02 4.05] meter 7.07 meter 278.3464566929134 inch [3.02 4.05] meter [length] Die Dokumentation zu pint findet sich auf [https://pint.readthedocs.io/en/stable/]. =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. <small>Je größer die Anzahl der Datensätze n wird, umso weniger spielt das eine Rolle.</small> == 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. = Pandas kurz erklärt = Hier wird kurz auf das externe Modul Pandas eingegangen. Pandas steht für '''Pan'''el '''Da'''ta und ist ein Modul für die Datenanalyse und -manipulation. == Series == Vorstellbar ist eine "Series" wie eine einzelne Spalte in einem Tabellenkalkulationsprogramm. Beispiel (Index implizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) # Temperaturwerte print(temp) Ausgabe: 0 8.5 1 11.0 2 12.5 3 10.0 4 13.0 5 11.0 6 14.5 dtype: float64 Das Kürzel <code>pd</code> ist Konvention. Es seien Temperaturwerte gegeben. Die erste Spalte bei der Ausgabe ist der Index. Die zweite Spalte enthält die Werte. Beispiel (separater Zugriff auf den Index und auf die Werte): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) print(temp.index) print(temp.values) Ausgabe: RangeIndex(start=0, stop=7, step=1) [ 8.5 11. 12.5 10. 13. 11. 14.5] Beispiel (Index explizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) print("*** Die gesamte Series ***") print(temp) print("*** Temperatur am Donnerstag ***") print(temp["Do"]) print("*** Tage wärmer als 12 Grad ***") temp_gr_12 = temp[temp > 12] print(temp_gr_12) Ausgabe: *** Die gesamte Series *** Mo 8.5 Di 11.0 Mi 12.5 Do 10.0 Fr 13.0 Sa 11.0 So 14.5 dtype: float64 *** Temperatur am Donnerstag *** 10.0 *** Tage wärmer als 12 Grad *** Mi 12.5 Fr 13.0 So 14.5 dtype: float64 == DataFrame == Ein DataFrame entspricht einer Tabelle. Beispiel (Erzeugen eines DataFrames): import pandas as pd tag = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] v1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=tag) v2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=tag) v3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=tag) tab = pd.concat([v1, v2, v3], axis=1) tab.columns = stadt print(tab) print(type(tab)) Ausgabe: Linz Salzburg Innsbruck Mo 1 8 Hallo Di 2 9 16 Mi 3 10 17 Do 4 11 18 Fr 5 12 19 Sa 6 13 20 So 7 14 21 <class 'pandas.core.frame.DataFrame'> Wir haben hier drei Datenspalten (v1, v2, v3) mit den Spaltennamen "Linz", "Salzburg" und "Innsbruck", sowie sieben Zeilen "Mo" bis "So". Beispiel (Zugriff auf eine Spalte): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab["Salzburg"]) Ausgabe: Mo 8 Di 9 Mi 10 Do 11 Fr 12 Sa 13 So 14 Name: Salzburg, dtype: int64 Beispiel (Zugriff auf eine Zeile): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab.loc["Di"]) Ausgabe: Linz 2 Salzburg 9 Innsbruck 16 Name: Di, dtype: object == Grafiken erstellen == Hier sei nur ein Torten- oder Kreisdiagramm gezeichnet: import pandas as pd import matplotlib.pyplot as plt temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) expl = [0, 0.2, 0, 0, 0, 0, 0] temp.plot.pie(figsize=(5,5), explode=expl) plt.show() Ausgabe: [[Datei:IngPython_pandas1.svg|350px]] Die <code>figsize</code> hat nichts mit der Spaltenanzahl zu tun, sondern setzt die Fenster- oder Diagrammgröße in Inches (Breite * Höhe). Mit Pandas kann man auch noch andere Diagrammarten erstellen, z.B. Linien- oder Balkendiagramme. Dies wird hier aber nicht vorgeführt. == Arbeiten mit CSV- und LibreOffice-Dateien == Gegeben sei eine CSV-Datei test1.csv: Mai;Juni;Juli 12;17;45 15;65;65 16;32;23 4;14;7 23;17;Keine Daten verfügbar 67;Keine Daten verfügbar;45 15;24;12 Lesen von dieser CSV-Datei: import pandas as pd tab = pd.read_csv("test1.csv", sep=";") print(tab) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Schreiben in eine CSV-Datei test2.csv: import pandas as pd val = pd.read_csv("test1.csv", sep=";") val.to_csv("test2.csv", index=False) Ausgabedatei: Mai,Juni,Juli 12,17,45 15,65,65 16,32,23 4,14,7 23,17,Keine Daten verfügbar 67,Keine Daten verfügbar,45 15,24,12 Die Ausgabedatei ist beinahe gleich wie die Eingabedatei. Es werden aber Kommas statt Strichpunkten verwendet. Daher kommt auch der Name CSV ('''C'''omma '''S'''eparated '''V'''alue). Will man wieder Strichpunkte, so kann man dies über den Parameter <code>sep=";"</code> in der <code>to_csv</code>-Funktion machen. Um direkt von einer ODS-Datei (LibreOffice Calc, '''O'''pen '''D'''ocument '''S'''preadsheet) zu lesen muss das Modul odfpy extra installiert sein (per YaST, pip etc.). Gelesen wird dann folgendermaßen: import pandas as pd df = pd.read_excel('test1.ods', engine='odf') print(df) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Die Datei "test1.ods" sei die Datei, aus der das obige "test1.csv" generiert wurde. Schreiben kann man ebenfalls in eine ODS-Datei: import pandas as pd daten = { "Name": ["Hugo", "Richie", "Wilhelmine"], "Alter": [38, 30, 29], "Ort": ["Linz", "Innsbruck", "Salzburg"] } tab = pd.DataFrame(daten) with pd.ExcelWriter("test2.ods", engine="odf") as writer: tab.to_excel(writer, sheet_name="Daten", index=False) Es ergibt sich eine "test2.ods"-Datei (Screenshot): [[Datei:IngPython_pandas2.png|250px]] == Sonstiges == Hier seien stichpunktartig einige weitere Features von Pandas angeführt: * {{W|Pivot-Tabelle|Pivot-Tabellen}} (pivot ... Dreh-, Angelpunkt): Zweck - große Datenmengen schnell zusammenfassen, filtern und analysieren. * NaN ('''n'''ot '''a''' '''n'''umber): In einem Datensatz fehlt ein Wert oder ist nicht definiert. * groupby-Funktion: Daten strukturieren, aggregieren oder Zwischensummen bilden. * {{W|Binning#Datenverarbeitung_und_-analyse|Binning}}: Klassenbildung. * Mehrstufige Indizierung (Hierarchical Indexing oder MultiIndex) * Zeitreihen (Time Series) Für tiefergehende und weiterführende Informationen wird auf das Buch von ''Klein: Numerisches Python; Hanser, 2023'' und [https://pandas.pydata.org/ pandas] verwiesen. = 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) Das funktioniert übrigens nicht nur bei Funktionsannotationen, sondern auch mit Variablen etc. from typing import Final # "Konstante", ab Python 3.8 NUMBER: Final = 123 print(NUMBER) NUMBER = 125 print(NUMBER) # Variable a: int = 345 print(a) a = "Irgendwas" print(a) # Variable (Pipe-Symbol), ab Python 3.10 b : int|str = 567 print(b) b = "sonstiges" print(b) Ausgabe mit python: 123 125 345 Irgendwas 567 sonstiges Ausgabe mit mypy: test3.py:6: error: Cannot assign to final name "NUMBER" [misc] test3.py:12: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] Found 2 errors in 1 file (checked 1 source file) Python kennt übrigens keine Konstanten. Man kann diese mittels Großbuchstaben andeuten. Das ist aber nur Konvention, hat aber keine sonstigen Auswirkungen. Oder man verwendet wie oben gezeigt <code>Final</code>. Dann werfen Typchecker Errors aus. ==pydoc== pydoc ist ein Werkzeug, um Python-Module, -Funktionen und -Klassen zu dokumentieren. Beispiel <code>test1.py</code>: """Rechen-Modul. Funktionen die von pydoc gelesen werden. """ def multiply(a, b): """Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a * b def add(a, b): """Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a + b if __name__ == '__main__': print(multiply(3, 5)) print(add(4, 6)) Ausgabe mit <code>python3.13 -m pydoc test1</code> auf der Konsole: Help on module test1: NAME test1 - Rechen-Modul. DESCRIPTION Funktionen die von pydoc gelesen werden. FUNCTIONS add(a, b) Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl multiply(a, b) Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl FILE /home/xyz/tmp/test1.py Siehe zu diesem Thema z.B. auch [https://docs.python.org/3/library/pydoc.html]. Mittels <code>python3.13 -m pydoc -w test1</code> kann man auch eine HTML-Dokumentation erstellen. Wofür <code>if __name__ == '__main__':</code> steht, siehe z.B. etwas später im Abschnitt [[Ing_Mathematik:_Python#Doctests]]. == 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.3 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.5.1 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://build123d.readthedocs.io/en/latest/ build123d] * [https://docs.vtk.org/en/latest/api/python.html VTK] * [https://docs.bokeh.org/en/latest/docs/first_steps.html bokeh] * [https://py5coding.org/ py5] == 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] * [https://pint.readthedocs.io/en/stable/ pint] = 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] * Iacono et al.: Programmieren trainieren; 3. Aufl., Hanser, 2023, ISBN 978-3-446-47766-7 * 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}} r4khlcmo6av6mbodk2os52em3arbbad 1092071 1092070 2026-10-11T11:12:41Z Intruder 1513 /* Rekursive Funktionen */ 1092071 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 (welche Verzeichnisse im PATH liegen, kann bspw. mit <code>echo $PATH</code> abgefragt werden). 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 == === Tests === 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) === Popularität === * An Beliebtheit rangiert Python mit Stand September 2026 mit einem Rating von 17,76% 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 Q1/2026 auf Rang 2, vor TypeScript und hinter JavaScript. * [https://pypl.github.io/PYPL.html PYPL] (Popularity of Programming Language Index) ist ein anderer Popularitätsindex für Programmiersprachen. Auch hier liegt Python mit großem Vorsprung auf dem ersten Platz (52,08%), gefolgt von Java (13,34%) und C/C++ (7,99%) (Stand September 2026). === Namensgebung === 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 in PyConUS24.jpg|{{W|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 <code>help</code> oder <code>help()</code> 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, bzw. der Eingabe von q, quit oder exit mit anschließendem Drücken der RETURN-Taste. == 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 Theoretisch kann man auch Umlaute, griechische Buchstaben u.ä. in Variablenbezeichnern nutzen, z.B. ößÜ = 16 α = 134 print(ößÜ) print(α) Ausgabe: 16 134 Davon wird aber aus offensichtlichen Gründen ({{W|Englisch als Lingua franca}}) abgeraten. Das Eurozeichen (€) etc. darf man ohnehin nicht in Variablenbezeichnern verwenden, dies ergäbe einen Syntaxfehler. Siehe dazu auch [https://peps.python.org/pep-3131/]. 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")) # new style string formatting print("Hallo {:s}" . format("Hugo")) # new style string formatting print("Hallo %s" % "Hugo") # %-String 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 f-Strings (ab Python 3.6) sind die modernere Variante gegenüber %-Strings oder das ''new style string formatting''. Sie sind in der Regel auch schneller. 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. Man beachte, dass {{W|Rundungsfehler}} auftreten können. D.h. man sollte bei der Verwendung von {{W|Gleitkommazahl|Gleitpunktzahlen}} niemals auf absolute Genauigkeit prüfen, sondern eine Toleranz (einen <math>\varepsilon</math>-Wert) vorgeben. Ein klassisches Beispiel, das ein falsches Ergebnis liefert, ist: i = 0.1 + 0.2 print(i) if i == 0.3: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 NOK Und das, obwohl man schon in der Volksschule lernt, dass 0.1 + 0.2 gleich 0.3 ist. Korrekt muss das obige Beispiel z.B. so umgeschrieben werden: ist = 0.1 + 0.2 soll = 0.3 epsilon = 0.000001 print(ist) if abs(ist-soll) < epsilon: print("OK") else: print("NOK") Ausgabe: 0.30000000000000004 OK Alternativ kann auch die <code>math.isclose()</code>-Funktion verwendet werden. Das sei aber hier nur erwähnt und nicht konkret ausgeführt. <u>Aufgabe:</u> Recherchieren Sie im Internet zum Thema <code>math.isclose()</code>-Funktion. === 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 == Kopfgesteuerte Schleife, Endlosschleife und fußgesteuerte Schleife: [[File:Programmingloops.svg|450px]] === 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 Der <code>break</code>-Befehl wird etwas später noch einmal kurz thematisiert. === 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!") print("Setze Programm fort!") Ausgabe: Das Ergebnis ist unendlich Setze Programm fort! 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). Danach wird das Programm fortgesetzt. 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. Neu in Python 3.14 ist, dass mehrere Ausnahmen auch ohne Klammern angegeben werden können, z.B.: bis inkl. Python 3.13 nur: except (ZeroDivisionError, ValueError): ab Python 3.14 auch: except ZeroDivisionError, ValueError: == 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 c, d = mathFunc(3, 5) # Ausgabe der zurückgegebenen Werte print(c) print(d) Ausgabe: 8 15 Es werden r1 und r2 zurückgegeben und den Variablen c und d zugewiesen. (r1, r2) und (c, d) sind Tupel. Was Tupel konkret sind, siehe etwas später in diesem Kapitel. 3 und 5 sind die aktuellen Parameter (die Argumente). a und b sind die formalen Parameter. Die Zuweisung der aktuellen zu den formalen Parametern erfolgt in der gegebenen Reihenfolge, also die Zahl 3 wird an a übergeben und die Zahl 5 an b. Vorgabeparameter, z.B.: def mathFunc(a=10, b=20): r1 = a + b r2 = a * b return r1, r2 c, d = mathFunc(3, 5) print(c) print(d) e, f = mathFunc(5) print(e) print(f) g, h = mathFunc(b=6) print(g) print(h) Ausgabe: 8 15 25 100 16 60 Erklärung: * Erster <code>mathFunc</code>-Aufruf: Es werden, wie weiter oben, die Argumente 3 und 5 an die formalen Parameter a und b übergeben. Die Rückgabewerte r1 und r2 werden an c und d übergeben. Somit ist c=8 und d=15. * Zweiter <code>mathFunc</code>-Aufruf: Es wird nur das Argment 5 an a übergeben. b behält den Vorgabewert 20. D.h. e= 25 und f=100. * Dritter <code>mathFunc</code>-Aufruf: Es wird nur die Zahl 6 an b übergeben, a behält seinen Vorgabewert 10. Somit ergibt sich g=16 und h=60. Beispiel (Schlüsselwortargumente vs. Positionsargumente): def mathFunc(a, b): r1 = a - b r2 = a / b return r1, r2 c, d = mathFunc(b=3, a=5) # Schlüsselwortargumente e, f = mathFunc(3, 5) # Positionsargumente print(c) print(d) print(e) print(f) Ausgabe: 2 1.6666666666666667 -2 0.6 Durch die Verwendung von Schlüsselwortargumenten können die Argumente in beliebiger Reihenfolge angegeben werden. Bei den Positionsargumenten ist die Reihenfolge vorgegeben. Daraus resultiert auch der Unterschied bei den obigen Ausgaben. Werden beide Arten in einem Aufruf gemischt, so müssen die Positionsargumente zuerst angegeben werden. Den Unterschied zwischen beiden Argumentarten zu kennen ist wichtig, nicht nur bei selbst erstellten Funktionen. Auch beim Aufruf von Python-Funktionen verfolgt einen die Mischung von Schlüsselwort- und Positionsargumenten auf Schritt und Tritt, z.B. beim etwas später folgenden Abschnitt über "Grafiken zeichnen": plt.plot(x, y1, label = "cosh(x) + 2**x", lw=5, ls="dotted") Bei dieser <code>plot</code>-Funktion aus dem Modul <code>matplotlib.pyplot</code> sind <code>x</code> und <code>y1</code> Positionsargumente. <code>label, lw</code> und <code>ls</code> sind die Namen der Schlüsselwortargumente. Die Schlüsselwortargumente könnten auch in anderer Reihenfolge angeschrieben werden, nicht jedoch die Positionsargumente! Siehe zu <code>matplotlib.pyplot.plot</code> auch die Seite [https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html]. == 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 {{W|Rekursion|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 Typische Beispiele für die Abarbeitung als rekursive Funktionen sind die {{W|Fibonacci-Folge}} und die {{W|Fakultät_(Mathematik)|Fakultät}}. Jedoch ist der iterative Ansatz (mit konventionellen Schleifen) meist schneller und speicherplatzschonender. == 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 und Listen == 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. 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. Nachfolgend werden einige Operationen mit Listen und Tupel dargestellt. # 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 (eine Liste von Wörtern zu einem einzigen zusammenhängenden Text verbinden): woerter = ["Hallo", "Welt"] satz = " ".join(woerter) print(satz) Ausgabe: Hallo Welt == Dictionaries und Mengen == 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'> Leere Mengen lassen sich mit <code>set()</code> erzeugen: menge = set() # leere Menge print(menge) menge.add("Hallo") # Element hinzufügen print(menge) Ausgabe: set() {'Hallo'} Beispiel (einige Mengenoperationen): mengeA = {"Hallo", "Welt"} mengeB = {1, 2, "Welt", 3} # Vereinigung print(mengeA | mengeB) print(mengeA.union(mengeB)) # Durchschnitt print(mengeA & mengeB) print(mengeA.intersection(mengeB)) # Differenz print(mengeA - mengeB) print(mengeA.difference(mengeB)) # Symmetrische Differenz print(mengeA ^ mengeB) print(mengeA.symmetric_difference(mengeB)) # Teilmenge print(mengeA.issubset(mengeB)) # Mehrere Elemente hinzu mengeA.update(["Super", 67, 125]) print(mengeA) # Element löschen mengeA.discard("Hallo") print(mengeA) Ausgabe: {1, 2, 3, 'Welt', 'Hallo'} {1, 2, 3, 'Welt', 'Hallo'} {'Welt'} {'Welt'} {'Hallo'} {'Hallo'} {1, 2, 3, 'Hallo'} {1, 2, 3, 'Hallo'} False {67, 'Super', 'Welt', 'Hallo', 125} {67, 'Super', 'Welt', 125} Es gibt schon seit "ewigen" Zeiten unveränderbare Mengen, die mittels <code>frozenset</code> eingeleitet werden. Neu ab Python 3.15 ist dagegen das Pendant <code>frozendict</code>, das unveränderbare Dictionaries darstellt. fdict = frozendict({"vorname":"Hugo", "nachname":"Meister"}) fset = frozenset({"A", 1, "A"}) print(fdict) print(fdict["vorname"]) print (fset) try: fdict["vorname"] = "Max" print(fdict["vorname"]) except TypeError as e: print(f"Fehler: {e}") try: fset.add("B") print(fset) except AttributeError as e: print(f"Fehler: {e}") Ausgabe: frozendict({'vorname': 'Hugo', 'nachname': 'Meister'}) Hugo frozenset({1, 'A'}) Fehler: 'frozendict' object does not support item assignment Fehler: 'frozenset' object has no attribute 'add' == 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: x = ("a", "b", "c", "d", "e") print(x[2]) print(x[-3]) print(x) print(x[:]) print(x[2:]) print(x[:2]) print(x[::-1]) Ausgabe: c c ('a', 'b', 'c', 'd', 'e') ('a', 'b', 'c', 'd', 'e') ('c', 'd', 'e') ('a', 'b') ('e', 'd', 'c', 'b', 'a') Visualisierung: '''a''' '''b''' '''c''' '''d''' '''e''' +-----------------------------+ | 0 | 1 |//'''2'''//| 3 | 4 | +-----------------------------+ | -5 | -4 |/'''-3'''//| -2 | -1 | +-----------------------------+ |/////////////////////////////| +-----------------------------+ |/////////////''':'''///////////////| +-----------------------------+ | | |///////'''2:'''////////| +-----------------------------+ |////''':2'''/////| | | | +-----------------------------+ |\\\\\<--\\\\\'''::-1'''\\\\<--\\\\\| +-----------------------------+ Beispiel: x = ("a", "b", "c", "d", "e") print(x[2::2]) print(x[2::-1]) print(x[2:0:-1]) Ausgabe: ('c', 'e') ('c', 'b', 'a') ('c', 'b') Hier sieht man sehr schön, dass beim letzen <code>print</code>-Befehl die 0, die in diesem Fall den Buchstaben "a" repräsentiert, als letzer Wert nicht mehr dazugehört (wie weiter oben angedeutet ist der End- oder Stoppwert exklusiv). Will man den Buchstaben "a" dazunehmen, so ist die vorletzte Zeile die richtige. Beispiel: Umdrehen von Strings str1 = "Hallo" str2 = str1[::-1] print(str2) Ausgabe: ollaH Mit zweidimensionalen Listen/Tupels wird das Ganze schon bedeutend komplexer. Beispiel: Verkettetes Slicing A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] print(A[2::][::2]) Ausgabe: [['20', '21', '22', '23'], ['40', '41', '42', '43']] Das erste Slicing erstellt folgenden Slice: [["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] Das zweite Slicing wird dann auf diesen Slice angewendet und ergibt das genannte Endergebnis. Will man Spalten statt Zeilen, so funktioniert dieser direkte Weg nicht. Man verwendet dann List Comprehensions oder die NumPy-Bibliothek. Diese Bibliothek erlaubt zweidimensionales Slicing. Sie wird erst später bei den Grafiken eingeführt. Deshalb wird hier dazu noch kein Beispiel gezeigt. = 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 (ist der sogenannte Konstruktor). 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 strikt/stark private ist. Wird nur ein Unterstrich verwendet, so deutet dies auf schwach/weakly private hin. Letzteres wird in Python deutlich häufiger verwendet. Bei den Methoden wird immer self als erster Parameter angegeben. Beim Aufruf der entsprechenden Funktion wird das self aber nicht berücksichtigt. Besonderheiten bei stark und schwach private: * '''stark private''': ** zwei führende Unterstriche ** Name Mangling: Name wird zu <code>_Klassenname__variablenname</code> ** Schutz vor Namenskollisionen bei Vererbung ** am Ende des Namens dürfen keine zwei Unterstriche verwendet werden. Das wären dann Magic Methods oder Dunder-Methods (z.B. __init__). Diese sind public. * '''schwach private''': ** ein führender Unterstrich ** Name bleibt gleich ** ist nur ein Hinweis an die Programmierer, kein starker Schutz ** <code>from modul import *</code>: Objekte mit einem führenden Unterstrich werden standardmäßig nicht importiert ** Standard für die Kapselung bei Python. Schätzung: Weit mehr als 80% der nicht-öffentlichen Attribute/Methoden werden als schwach private deklariert. Weniger als 5-10% als stark private (Quelle: Google KI) == 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 == === Einfachvererbung === [[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 === Mehrfachvererbung === Mit Python ist auch Mehrfachvererbung möglich, z.B.: class A: def _printA(self): print("Klasse A") class B: def _printB(self): print("Klasse B") class C(A,B): def __init__(self): self._printA() self._printB() c = C() Ausgabe: Klasse A Klasse B Die Klasse C erbt von A und B. In UML sieht das in etwa so aus: +-----------------------------+ +-------------------------+ | A | | B | +-----------------------------+ +-------------------------+ | | | | +-----------------------------+ +-------------------------+ | # printA() | | # printB() | +-----------------------------+ +-------------------------+ . . / \ / \ --- --- | | | | +---------------------+ | C | +---------------------+ | | +---------------------+ | | +---------------------+ <code>printA()</code> und <code>printB()</code> sind schwach private (in UML als <code>protected</code> mit einem # dargestellt). Es ist aber zu beachten, dass schwach private nur Konvention ist. Praktisch könnte man auch von außerhalb auf diese Methoden zugreifen. Das soll man aber nicht tun, und das wird in Python durch einen Unterstrich angedeutet. == Statische Attribute, statische Methoden und Klassenmethoden == Beispiel: class Math: pi = 3.14159265 cnt = 0 @staticmethod def expo(): return 2.71828183 @classmethod def counterPlus(cls): cls.cnt += 1 print(Math.pi) print(Math.expo()) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Math.counterPlus() print(Math.cnt) Ausgabe: 3.14159265 2.71828183 1 2 3 Statische Attribute und Methoden, bzw. Klassenmethoden, sind direkt einer Klasse zugeordnet und nicht einem Objekt. Der Unterschied zwischen statischen Methoden und Klassenmethoden ist, dass statische Methoden nicht auf Klassenattribute (bzw. statische Attribute) zugreifen können, Klassenmethoden aber schon. <code>pi</code> und <code>cnt</code> sind statische Attribute. <code>expo()</code> ist eine statische Methode. <code>counterPlus(cls)</code> ist eine Klassenmethode. Die Bezeichnung des Parameters als <code>cls</code> ist reine Konvention, man sollte sich aber daran halten. = 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. Die "arange"-Funktion aus der NumPy-Bibliothek wird später ein bisschen genauer behandelt (siehe [[Ing_Mathematik:_Python#Zahlenfolgen|Zahlenfolgen]]). <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. Die Farbe des Graphen kann man über den <code>color</code>-Schalter einstellen, z.B. auf rot setzen: plt.plot(x, y, color="red") Das ist nur eine Möglichkeit, es gibt auch andere, z.B. mittels * hexadezimaler Zahl, z.B. <code>color="#ff0000"</code> * Kurzform, z.B. <code>"r"</code> Statt <code>color</code> kann auch die Kurzform <code>c</code> verwendet werden. <u>Kleine Aufgabe:</u> Probieren Sie diese "Farbspielereien" am Computer aus. === Graphen mehrerer Funktionen und weiteres === ==== Mehrere Funktionen in einen Plot zeichnen ==== 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]] Einige weitere Bespiele zu <code>ls</code></code>: * - ... durchgezogene Linie * -. ... Strichpunkt-Linie * dashed ... gestrichelte Linie ==== Marker ==== Desweiteren kann man Marker verwenden. Beispiel: import matplotlib.pyplot as plt plt.plot(10, 10, "ob", markersize=30) plt.plot(20,20, ".r") plt.plot(30,30, "Dg", ms=15) plt.plot(40, 40, "ok", ms=20, mec="#FF5733", mew=3) plt.plot(50, 50, "vk", ms=20, mec="yellow", mew=3) plt.grid() plt.show() Das erste Zeichen im String nach den Koordinaten im plot-Befehl symbolisiert die Form (z.B. o für einen Kreis). Das zweite Zeichen steht für die Farbe (z.B. b für blue). <code>markersize</code> oder <code>ms</code> setzt die Größe des Markers. Ausgabe: [[Bild:PythonIng_formatpara1.svg|200px]] Einige weitere Marker: * * ... Stern * + ... Plus * h oder H ... Sechseck Man kann mit den Parametern * <code>markeredgecolor</code> oder kurz <code>mec</code> die Farbe des Markerrandes * <code>markeredgewidth</code> oder kurz <code>mew</code> die Dicke des Randes * <code>markerfacecolor</code> oder kurz <code>mfc</code> die Innenfarbe des Markers bestimmen. === 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]] === Anmerkungen hinzufügen === Anmerkungen (engl. annotations) lassen sich mit matplotlib einfach hinzufügen. Unter Anmerkung wird hier ein Pfeil mit Text verstanden. Beispiel: import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot([1,10], [1,10]) ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0)) plt.show() Ausgabe: [[Datei: PythonIng_ann1.svg|400px]] <code>shrink</code> verkürzt den Pfeil. Sinnvoll sind beim obigen Beispiel Werte irgendwo zwischen 0.0 und 0.3. <u>Kleine Aufgabe:</u> Probieren Sie verschiedene Pfeillängen aus. Die Schriftgröße verändern können Sie übrigens mit <code>fontsize</code> oder <code>size</code>, z.B. ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops=dict(facecolor="red", shrink=0.0), fontsize=25) Eine andere Schreibweise für Dictionaries, die wir weiter oben schon kennengelernt haben, die wir aber nochmals explizit aufschreiben wollen, ist ax.annotate("Test", xy=(2, 2), xytext=(3, 5), arrowprops={"facecolor":"red", "shrink":0.0}) === Figure, Axes etc.=== [[Bild:PythonIng_figure33.svg|350px]] * fig (figure) ... das übergeordnete Fenster * ax (axes) ... "Plot", das eigentliche Zeichenfenster * axis ... die Koordinatenachsen * spine ... Begrenzungslinien des Zeichenfensters * tick ... Markierung an den Achsen des Zeichenfensters Beispiel (eine figure mit 6 axes, ax-Titel, y-axis-Titel): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=3, ncols=2) ax[0][0].text(0.1, 0.5, "11") ax[0][1].text(0.1, 0.5, "12") ax[1][0].text(0.1, 0.5, "21") ax[1][1].text(0.1, 0.5, "22") ax[2][0].text(0.1, 0.5, "31") ax[2][1].text(0.1, 0.5, "32") ax[0][0].set(ylabel="y-label", title="axes 1") plt.show() [[Bild:PythonIng_figure34.png|350px]] Beispiel (Achsenbereiche setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-1, 10, 1, 12)) plt.show() [[Bild:PythonIng_figure35.svg|350px]] Beispiel (Gitter einschalten): import matplotlib.pyplot as plt fig, ax = plt.subplots(nrows=1, ncols=2) ax[0].text(5, 5, "Hallo Welt") ax[1].text(.5, .5, "Servus") ax[0].axis((-5, 10, 1, 12)) ax[0].minorticks_on() ax[0].grid(which='minor') ax[1].grid(which='major') plt.show() [[Bild:PythonIng_figure36.svg|350px]] Beispiel (ticks setzen) import matplotlib.pyplot as plt import matplotlib.ticker as tick fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") pos = [0, .2, .5] lab = ["a", "b", "c"] ax.xaxis.set_major_locator(tick.FixedLocator(pos)) ax.xaxis.set_major_formatter(tick.FixedFormatter(lab)) plt.show() [[Bild:PythonIng_figure37.svg|350px]] Beispiel (Achsenkreuz beliebig setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.text(0.1, 0.5, "11") ax.spines['top'].set_color('none') ax.spines['right'].set_color('none') ax.spines['bottom'].set_position(('data', 0.3)) ax.spines['left'].set_position(('data', 0.5)) plt.show() [[Bild:PythonIng_figure38.svg|350px]] Beispiel (Inset): import matplotlib.pyplot as plt # Hauptplot erstellen fig, ax = plt.subplots() ax.text(0.1, 0.1, "Hallo") # Inset hinzu: axin = ax.inset_axes([0.4, 0.4, 0.3, 0.3]) axin.text(0, 0, "Servus") axin.plot() plt.show() [[Bild:PythonIng_figure40.svg|350px]] Beispiel (Hintergrundfarben setzen): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.set_facecolor("green") fig.set_facecolor("yellow") plt.plot([1, 3], [6, 8]) plt.show() [[Bild:PythonIng_figure39.svg|300px]] === GridSpec === Mit figures und axes lassen sich schon jede Menge an Grafikdetails einstellen. GridSpec ist dann der Turbo oder Raketenantrieb den matplotlib eingebaut hat. Naja, vielleicht ist das etwas übertrieben, aber GridSpec ist sozusagen ein flexibler Layout-Manager. Bisher haben wir immer gleich große axes gezeichnet. Mit GridSpec können wir Subplots über mehrere Zeilen und Spalten ziehen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) gs = fig.add_gridspec(3, 3) ax1 = fig.add_subplot(gs[0, :]) ax1.set_title("Bereich 1: gs[0, :]") ax2 = fig.add_subplot(gs[1:, 0]) ax2.set_title("Bereich 2: gs[1:, 0]") ax3 = fig.add_subplot(gs[1, 1:]) ax3.set_title("Bereich 3: gs[1, 1:]") ax4 = fig.add_subplot(gs[2, 1:]) ax4.set_title("Bereich 4: gs[2, 1:]") plt.show() [[Bild:PythonIng_gridspec1.svg|350px]] Erklärung zu <code>(gs[zeile, spalte])</code>: Slicing; damit legen Sie fest, welche Zellen ein Subplot belegt. Achtung! Die Indizes beginnen bei 0 und nicht bei 1 (d.h., die erste Zeile/Spalte hat den Index 0) * : ... alle * 1: ... 1 bis Ende * etc. <code>constrained_layout=True</code> sorgt dafür, dass sich die Subplots nicht überlappen. Aber auch die Spaltenbreiten und Zeilenhöhen lassen sich anpassen. Beispiel: import matplotlib.pyplot as plt import matplotlib.gridspec as gs fig = plt.figure(constrained_layout=True) # Verhältnis erste zu zweite Zeile = 1.5:1 # Verhältnis erste zu zweite Spalte = 1:4 gs = fig.add_gridspec(2, 2, figure=fig, height_ratios=[1.5, 1], width_ratios=[1, 4]) ax1 = fig.add_subplot(gs[0, 0]) ax1.set_title("Bereich 1: gs[0, 0]") ax2 = fig.add_subplot(gs[0, 1]) ax2.set_title("Bereich 2: gs[0, 0]") ax3 = fig.add_subplot(gs[1, 0]) ax3.set_title("Bereich 3: gs[1, 0]") ax4 = fig.add_subplot(gs[1, 1:]) ax4.set_title("Bereich 4: gs[1, 1]") plt.show() [[Bild:PythonIng_gridspec2.svg|350px]] Auch die Ränder lassen sich einstellen. Beispiel 1: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.2, hspace=0.2) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec3.svg|300px]] * hspace ... height space, vertikaler Abstand zwischen den Zeilen * wspace ... width space, horizontaler Abstand zwischen den Spalten Und im Vergleich dazu Beispiel 2: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec fig = plt.figure() gs = gridspec.GridSpec(2, 2, left=0.3, right=0.7, bottom=0.3, top=0.7, wspace=0.3, hspace=0.3) ax1 = fig.add_subplot(gs[0, 0]) ax2 = fig.add_subplot(gs[1, 0]) ax3 = fig.add_subplot(gs[0, 1]) ax4 = fig.add_subplot(gs[1, 1]) plt.show() [[Bild:PythonIng_gridspec4.svg|300px]] Es gibt noch einige weitere Details, die sich mit GridSpec variieren lassen (z.B. SubGridSpec ... verschachtelte Grid-Layouts). Aber wir belassen es vorerst dabei. Das bisher Gezeigte sollte für die meisten Probleme ausreichend sein. === 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|300px|center]] * 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|300px|center]] * 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. * Zeichnen Sie folgendes Bild (die Achsenbeschriftungen positionieren Sie z.B. mit den Befehlen <code>ax.set_xlabel("x", loc="right"); ax.set_ylabel("y", loc="top")</code>): [[Bild:PythonIng_aufgabe24.svg|400px|center]] * Zeichnen Sie mit Python, matplotlib und NumPy folgendes Bild: [[Bild:PythonIng_figure41.svg|400px|center]] * Zeichnen Sie mit GridSpec ein Layout, das in etwa wie folgendes aussieht (<code>height_ratios=[1, 2], width_ratios=[2, 1]</code>): [[Bild:PythonIng_aufgabe23.svg|300px|center]] == 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 und fortgeschrittenes 3D == === 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 build123d === Hiermit lassen sich einfach 3D-Modelle für CAD-Systeme erstellen. [https://build123d.readthedocs.io/en/latest/ build123d] ist ein externes Modul und muss vorab, wie schon für mehrere andere Module gezeigt, installiert werden (pip, venv etc.). Beispielcode: from build123d import * # Bauteil with BuildPart() as bauteil: # Zylinder erstellen Cylinder(100, 60) # Bohrung with BuildSketch(bauteil.faces().sort_by(Axis.Z)[-1]) as loch: Circle(radius=10) # negativ extrudieren extrude(amount=-60, mode=Mode.SUBTRACT) # Dem Bauteil z.B. die Farbe rot zuweisen # bauteil.part.color = Color("red", alpha=1.0) # Fertiges Modell exportieren export_step(bauteil.part, "bauteil.step") Ausgabe ({{W|Standard for the exchange of product model data | STEP}}-Datei, z.B. in {{W|FreeCAD}} importiert, Screenshot): [[Datei:PythonIng_build123d.jpg | 300px]] FreeCAD ist ein freies 3D-CAD-System. Die [[Ing_Mathematik:_Python#FreeCAD | Makroprogrammierung]] in diesem System wird später noch gezeigt. STEP steht für "'''St'''andard for the '''e'''xchange of '''p'''roduct model data" und ist ein Datenaustauschformat. === 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> == Sonstige Grafikbibliotheken == === Py5 === Im Buch ''Iacono et al.: Programmieren trainieren, mit über 150 Workouts in Java und Python; 3. Aufl., Hanser, 2023'' werden zahlreiche Aufgaben gestellt, die u.a. mit Processing zu lösen sind. Processing ist eine einfache Programmiersprache und Entwicklungsumgebung. Warum ich das erwähne? Weil in älteren Processing-Versionen auch die Programmiersprache Python 2 abgedeckt war. Bei neueren Versionen gibt es keine Python-Anbindung mehr (getestet nur mit openSUSE Tumbleweed und Python 3). Somit funktioniert der Python-Teil im genannten Buch zum Teil nicht mehr. Ist das tragisch, oder kann das Buch weggeschmissen werden? Nein! Es gibt Alternativen zu Processing. Zum einen die im Buch selbst genannten Bibliotheken tkinter oder pygame. Pygame wird dort auch kurz anhand eines Beispiels thematisiert. Aber noch besser ist es, die py5-Bibliothek zu verwenden (lt. Google KI vom 30. Sept. 2026: ''Der echte, moderne „spirituelle Nachfolger“''). Damit ist die Funktionalität von Processing gewährleistet. Man kann aber beliebige Editoren oder IDEs zur Programmerstellung verwenden, ohne an Processing als IDE gebunden zu sein. Nachfolgend werden ein paar Beispiele zu dieser externen Bibliothek gegeben. Der erste Schritt ist wieder, die py5-Bibliothek zu installieren (z.B. mittels pip in eine virtuelle Umgebung; Python>=3.10 und ein JDK>=17 müssen auch installiert sein (Stand Okt. 2026)). Dann kann es schon mit dem Programmieren losgehen. Beispiel (angelehnt an die ''Aufgabe W.2.3 Haus'' des genannten Buchs, jedoch etwas erweitert): import py5 def setup(): py5.size(600, 600) # Fenstergröße 600px x 600px py5.background(255, 255, 255) # Hintergrundfarbe ... weiss def draw(): # Haus zeichnen py5.fill(255, 255, 255) # Hintergrundfarbe ... weiss py5.stroke(255, 0, 0) # Linienfarbe ... rot py5.stroke_weight(3) # Linienbreite ... 3px py5.rect(150, 200, 300, 300) # Rechteck zeichnen (x, y, b, h) py5.triangle(150, 200, 300, 100, 450, 200) # Dreieck zeichnen py5.circle(250, 300, 70) # Kreis zeichnen (x, y, d) # Bemassung zeichnen py5.stroke(0, 255, 0) py5.stroke_weight(1) py5.line(300, 100, 550, 100) # Linie zeichnen py5.line(450, 200, 550, 200) py5.line(450, 500, 550, 500) py5.line(150, 500, 150, 550) py5.line(450, 500, 450, 550) py5.text_size(20) # Textgroesse py5.fill(0, 255, 0) # Textfarbe py5.text("300", 280, 550) # Text zeichnen py5.text("100", 500, 150) py5.text("300", 500, 350) # Gedrehtes Rechteck zeichnen py5.stroke(255, 255, 0) py5.stroke_weight(3) py5.fill(200, 200, 200) py5.translate(320, 260) py5.rotate(0.3) # Drehung (in Radiant) py5.rect(0, 0, 100, 50) # Startet den Sketch py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_1.png|300px]] Das skizzierte Haus ist mit einer rudimentären Bemaßung versehen und hat zwei Fenster, ein rundes und ein gedrehtes, trübes rechteckiges. Beispiel (Mausoperationen): import py5 def setup(): py5.size(600, 600) py5.background(255, 255, 255) def draw(): pass def mouse_pressed(): py5.text_size(15) py5.fill(0, 0, 255) x = py5.mouse_x y = py5.mouse_y py5.text("(%d, %d)" %(x, y), x, y) # Positionsdaten bei Maus-Klick schreiben py5.run_sketch() Ausgabe: [[Datei:PythonIng_py5_2.png|300px]] Obiges Bild zeigt das Resultat nach einigen Mausklicks. Man erkennt, dass links-oben der Koordinatennullpunkt ist. Die x-Achse zeigt nach rechts, die y-Achse nach unten. Beispiel (Fünfeck): import py5 import random def setup(): py5.size(400, 400) py5.no_loop() # statisches Bild (schont die Ressourcen) def draw(): py5.translate(py5.width / 2, py5.height / 2) # KS setzen radius = 150 eck_anzahl = 5 py5.fill(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # Füllfarbe auf Zufallswert setzen py5.stroke(155, 0, 0) py5.stroke_weight(5) # Fünfeck als Polygon zeichnen py5.begin_shape() for i in range(eck_anzahl): winkel = i * py5.TWO_PI / eck_anzahl - py5.HALF_PI x = py5.cos(winkel) * radius y = py5.sin(winkel) * radius py5.vertex(x, y) py5.end_shape(py5.CLOSE) py5.run_sketch() Ausgabe, bspw.: [[Datei:PythonIng_py5_5.png|200px]] <u>Kleine Aufgaben:</u> * Um die Funktionsweise von <code>py5.no_loop()</code> zu verstehen, kommentieren Sie die Anweisung im Beispiel einfach aus, und starten Sie das Programm erneut. * Zeichnen Sie ein Siebeneck. * Was passiert, wenn man <code>py5.CLOSE</code> im <code>py5.end_shape()</code>-Befehl weglässt? Beispiel (Grafik laden und zeichnen): import py5 image1 = None def setup(): global image1 py5.size(600, 600) py5.no_loop() image1 = py5.load_image("PythonIng_py5_1.png") def draw(): py5.image(image1, 0, 0) py5.run_sketch() Hier wird einfach das Bild vom Haus-Beispiel (Screenshot von weiter oben) geladen und gezeichnet. Beispiel (angelehnt an ''W.5.10 Wurfparabel'' des genannten Buches; mit OOP erstellt): import py5 class Wurfparabel(py5.Sketch): def __init__(self, v0, angle): super().__init__() self.v0 = v0 self.angle = self.radians(angle) self.g = 9.81 self.t = 0.0 self.scale = 1.5 def settings(self): self.size(800, 600) def setup(self): self.background(0, 0, 0) def draw(self): self.t += 0.1 self.translate(50, 500) # KS Zeichnen self.fill(255, 255, 0) self.stroke(255, 255, 0) self.stroke_weight(1) self.line(0, 0, 0, -300) self.line(0, 0, 700, 0) # Punkt zeichnen cx = self.v0 * self.cos(self.angle) * self.t * self.scale cy = -(self.v0 * self.sin(self.angle) * self.t - \ self.g/2 * self.t**2) * self.scale self.fill(255, 0, 0) self.stroke(255, 0, 0) self.circle(cx, cy, 3) # Stoppen bei Bodenkontakt if cy >= -5: self.no_loop() def wurfparabel(v0=50, angle=45): sketch = Wurfparabel(v0, angle) sketch.run_sketch() v0 = float(input("v0 = ")) winkel = float(input("winkel = ")) wurfparabel(v0=v0, angle=winkel) Eingabe auf der Konsole: v0 = 60 winkel = 45 Ausgabe: [[Datei:PythonIng_py5_13.png|200px]] Die benötigten Formeln der Bewegungsgleichung kann man leicht herleiten, bzw. man benutzt folgende Grafik als Hilfestellung: [[Datei:Wurfparabel_Zusammenfassung_aktualisierung.png|700px]] <u>Aufgabe:</u> Berechnen Sie den Scheitelpunkt und die Wurfweite bei gegebener Anfangsgeschwindigkeit und gegebenem Abwurfwinkel. Die Abwurfhöhe <math>y_0</math> sei 0. Der Wurf soll auf der Erde stattfinden (g = 9.81 m/s²). Die dazu benötigten Formeln können aus obiger Grafik entnommen werden (oder Sie leiten sie selbst her). Geben Sie die Ergebnisse auf der Konsole aus. Beispiel (eine einfache Animation): import py5 i = 0 def setup(): py5.size(600, 600) py5.frame_rate(5) py5.stroke_weight(20) def draw(): global i py5.background(255, 255, 255) py5.translate(py5.width/2, py5.height/2) py5.rotate(py5.PI/8 * i) py5.line(-100, 0, 100, 0) i += 1 py5.run_sketch() Es wird eine rotierende Linie gezeichnet (z.B. wie bei einem Installationsprozess verwendet - zeigt an, dass der Installationsfortschritt nicht eingefroren ist). Hierbei ist nur aufzupassen, dass der Hintergrund in <code>draw()</code> gesetzt wird, ansonsten würden die Linien additiv gezeichnet. Das ist zwar auch schön, aber eben nicht das gewünschte Resultat. Hiermit belassen wir es mit der py5-Bibliothek und wenden uns wieder "mathematischeren" Themen zu. = 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> standardmäßig aber sehr wohl. Jedoch kann man das bei <code>linspace</code> auch ändern. Dann ist der Stoppwert nicht mehr enthalten: linspace(start, stop, num, endpoint=False) == 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 === Beispiel: 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 Visualisierung: 0 1 2 +-----------+ 0 | 1 | 2 | 3 | +-----------+ 1 | 4 | 5 | '''6''' | +-----------+ Beispiel: import numpy as np A = [["00", "01", "02", "03"], ["10", "11", "12", "13"], ["20", "21", "22", "23"], ["30", "31", "32", "33"], ["40", "41", "42", "43"]] B = np.matrix(A) print(B[2::, ::2]) Ausgabe: [['20' '22'] ['30' '32'] ['40' '42']] Mit der NumPy-Bibliothek funktioniert das zweidimensionale Slicing. Mit normalen Listen/Tupels geht das nicht, was wir schon früher thematisiert haben. === 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]. Beispiel: import numpy as np import scipy A = scipy.sparse.csr_array(np.eye(5)) print(A) Ausgabe: <Compressed Sparse Row sparse array of dtype 'float64' with 5 stored elements and shape (5, 5)> Coords Values (0, 0) 1.0 (1, 1) 1.0 (2, 2) 1.0 (3, 3) 1.0 (4, 4) 1.0 csr steht für '''c'''ompressed '''s'''parse '''r'''ow. Andere Möglichkeiten: * coo-Format ('''coo'''rdinate Format): <code>scipy.sparse.coo_array()</code> * lil-Format ('''li'''st of '''l'''ists): <code>scipy.sparse.lil_array()</code> * csc-Format ('''c'''ompressed '''s'''parse '''c'''olumn): <code>scipy.sparse.csc_array()</code> Es gibt auch <code>scipy.sparse.csr_matrix()</code> etc. Diese Befehle sind aber veraltet und es wird empfohlen, statt dessen die <code>..._array()</code>-Funktionen zu verwenden. <u>Aufgaben:</u> Recherchieren Sie im Internet * generell zum Thema "spärlich besetzte Matrizen mit Python". * Was sind die Vor-/Nachteile von csr-arrays gegenüber coo- und lil-arrays? * Wann verwendet man sinnvollerweise csr- und wann csc-arrays? * Wie wandelt man coo-arrays in csr-arrays um? = 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.] Siehe dazu auch das Kapitel [[Ing Mathematik: Numerisches Lösen von linearen Gleichungssystemen]]. Dort wird dieses Themengebiet u.a. mit weitergehenden Python-Skripten vertieft. == 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") <u>Aufgaben</u>: Ermitteln Sie mittels SymPy die <math>\mathcal{L}</math>-Transformierten und dann wieder die <math>\mathcal{L}^{-1}</math>-Transformationen (ähnlich wie beim obigen Beispiel): * <math>\sin(t) \circ\! -\! \bullet \ ?</math> * <math>t^3 \circ\! -\! \bullet \ ?</math> * <math>\text{e}^{\omega t} \circ\! -\! \bullet \ ?</math> =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 [https://mpmath.org/ 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). = pint - mit physikalischen Größen rechnen = [[File:Pint-Glas_UK.jpg|right|100px]] pint ist ein externes Python-Paket zur Manipulation von physikalischen Einheiten. Es muss vor der ersten Benutzung installiert werden (Vorgehensweise wie immer: pip, YaST, via venv etc.). Beispiel: import pint import numpy as np ureg = pint.UnitRegistry() # Einfache Berechnung print(3 * ureg.meter + 4 * ureg.cm) # Mit NumPy npeinzel = [3, 4] * ureg.meter + [2, 5] * ureg.cm print(npeinzel) npsum = np.sum(npeinzel) print(npsum) # Konvertierung von Einheiten npsum_inch = npsum.to(ureg.inch) print(npsum_inch) print(npeinzel.magnitude) print(npeinzel.units) print(npeinzel.dimensionality) Ausgabe: 3.04 meter [3.02 4.05] meter 7.07 meter 278.3464566929134 inch [3.02 4.05] meter [length] Die Dokumentation zu pint findet sich auf [https://pint.readthedocs.io/en/stable/]. =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. <small>Je größer die Anzahl der Datensätze n wird, umso weniger spielt das eine Rolle.</small> == 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. = Pandas kurz erklärt = Hier wird kurz auf das externe Modul Pandas eingegangen. Pandas steht für '''Pan'''el '''Da'''ta und ist ein Modul für die Datenanalyse und -manipulation. == Series == Vorstellbar ist eine "Series" wie eine einzelne Spalte in einem Tabellenkalkulationsprogramm. Beispiel (Index implizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) # Temperaturwerte print(temp) Ausgabe: 0 8.5 1 11.0 2 12.5 3 10.0 4 13.0 5 11.0 6 14.5 dtype: float64 Das Kürzel <code>pd</code> ist Konvention. Es seien Temperaturwerte gegeben. Die erste Spalte bei der Ausgabe ist der Index. Die zweite Spalte enthält die Werte. Beispiel (separater Zugriff auf den Index und auf die Werte): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5]) print(temp.index) print(temp.values) Ausgabe: RangeIndex(start=0, stop=7, step=1) [ 8.5 11. 12.5 10. 13. 11. 14.5] Beispiel (Index explizit vorgegeben): import pandas as pd temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) print("*** Die gesamte Series ***") print(temp) print("*** Temperatur am Donnerstag ***") print(temp["Do"]) print("*** Tage wärmer als 12 Grad ***") temp_gr_12 = temp[temp > 12] print(temp_gr_12) Ausgabe: *** Die gesamte Series *** Mo 8.5 Di 11.0 Mi 12.5 Do 10.0 Fr 13.0 Sa 11.0 So 14.5 dtype: float64 *** Temperatur am Donnerstag *** 10.0 *** Tage wärmer als 12 Grad *** Mi 12.5 Fr 13.0 So 14.5 dtype: float64 == DataFrame == Ein DataFrame entspricht einer Tabelle. Beispiel (Erzeugen eines DataFrames): import pandas as pd tag = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] v1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=tag) v2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=tag) v3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=tag) tab = pd.concat([v1, v2, v3], axis=1) tab.columns = stadt print(tab) print(type(tab)) Ausgabe: Linz Salzburg Innsbruck Mo 1 8 Hallo Di 2 9 16 Mi 3 10 17 Do 4 11 18 Fr 5 12 19 Sa 6 13 20 So 7 14 21 <class 'pandas.core.frame.DataFrame'> Wir haben hier drei Datenspalten (v1, v2, v3) mit den Spaltennamen "Linz", "Salzburg" und "Innsbruck", sowie sieben Zeilen "Mo" bis "So". Beispiel (Zugriff auf eine Spalte): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab["Salzburg"]) Ausgabe: Mo 8 Di 9 Mi 10 Do 11 Fr 12 Sa 13 So 14 Name: Salzburg, dtype: int64 Beispiel (Zugriff auf eine Zeile): import pandas as pd ind = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] stadt = ["Linz", "Salzburg", "Innsbruck"] temp1 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=ind) temp2 = pd.Series([8, 9, 10, 11, 12, 13, 14], index=ind) temp3 = pd.Series(["Hallo", 16, 17, 18, 19, 20, 21], index=ind) tab = pd.concat([temp1, temp2, temp3], axis=1) tab.columns = stadt print(tab.loc["Di"]) Ausgabe: Linz 2 Salzburg 9 Innsbruck 16 Name: Di, dtype: object == Grafiken erstellen == Hier sei nur ein Torten- oder Kreisdiagramm gezeichnet: import pandas as pd import matplotlib.pyplot as plt temp = pd.Series([8.5, 11.0, 12.5, 10.0, 13.0, 11.0, 14.5], index=["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]) expl = [0, 0.2, 0, 0, 0, 0, 0] temp.plot.pie(figsize=(5,5), explode=expl) plt.show() Ausgabe: [[Datei:IngPython_pandas1.svg|350px]] Die <code>figsize</code> hat nichts mit der Spaltenanzahl zu tun, sondern setzt die Fenster- oder Diagrammgröße in Inches (Breite * Höhe). Mit Pandas kann man auch noch andere Diagrammarten erstellen, z.B. Linien- oder Balkendiagramme. Dies wird hier aber nicht vorgeführt. == Arbeiten mit CSV- und LibreOffice-Dateien == Gegeben sei eine CSV-Datei test1.csv: Mai;Juni;Juli 12;17;45 15;65;65 16;32;23 4;14;7 23;17;Keine Daten verfügbar 67;Keine Daten verfügbar;45 15;24;12 Lesen von dieser CSV-Datei: import pandas as pd tab = pd.read_csv("test1.csv", sep=";") print(tab) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Schreiben in eine CSV-Datei test2.csv: import pandas as pd val = pd.read_csv("test1.csv", sep=";") val.to_csv("test2.csv", index=False) Ausgabedatei: Mai,Juni,Juli 12,17,45 15,65,65 16,32,23 4,14,7 23,17,Keine Daten verfügbar 67,Keine Daten verfügbar,45 15,24,12 Die Ausgabedatei ist beinahe gleich wie die Eingabedatei. Es werden aber Kommas statt Strichpunkten verwendet. Daher kommt auch der Name CSV ('''C'''omma '''S'''eparated '''V'''alue). Will man wieder Strichpunkte, so kann man dies über den Parameter <code>sep=";"</code> in der <code>to_csv</code>-Funktion machen. Um direkt von einer ODS-Datei (LibreOffice Calc, '''O'''pen '''D'''ocument '''S'''preadsheet) zu lesen muss das Modul odfpy extra installiert sein (per YaST, pip etc.). Gelesen wird dann folgendermaßen: import pandas as pd df = pd.read_excel('test1.ods', engine='odf') print(df) Ausgabe: Mai Juni Juli 0 12 17 45 1 15 65 65 2 16 32 23 3 4 14 7 4 23 17 Keine Daten verfügbar 5 67 Keine Daten verfügbar 45 6 15 24 12 Die Datei "test1.ods" sei die Datei, aus der das obige "test1.csv" generiert wurde. Schreiben kann man ebenfalls in eine ODS-Datei: import pandas as pd daten = { "Name": ["Hugo", "Richie", "Wilhelmine"], "Alter": [38, 30, 29], "Ort": ["Linz", "Innsbruck", "Salzburg"] } tab = pd.DataFrame(daten) with pd.ExcelWriter("test2.ods", engine="odf") as writer: tab.to_excel(writer, sheet_name="Daten", index=False) Es ergibt sich eine "test2.ods"-Datei (Screenshot): [[Datei:IngPython_pandas2.png|250px]] == Sonstiges == Hier seien stichpunktartig einige weitere Features von Pandas angeführt: * {{W|Pivot-Tabelle|Pivot-Tabellen}} (pivot ... Dreh-, Angelpunkt): Zweck - große Datenmengen schnell zusammenfassen, filtern und analysieren. * NaN ('''n'''ot '''a''' '''n'''umber): In einem Datensatz fehlt ein Wert oder ist nicht definiert. * groupby-Funktion: Daten strukturieren, aggregieren oder Zwischensummen bilden. * {{W|Binning#Datenverarbeitung_und_-analyse|Binning}}: Klassenbildung. * Mehrstufige Indizierung (Hierarchical Indexing oder MultiIndex) * Zeitreihen (Time Series) Für tiefergehende und weiterführende Informationen wird auf das Buch von ''Klein: Numerisches Python; Hanser, 2023'' und [https://pandas.pydata.org/ pandas] verwiesen. = 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) Das funktioniert übrigens nicht nur bei Funktionsannotationen, sondern auch mit Variablen etc. from typing import Final # "Konstante", ab Python 3.8 NUMBER: Final = 123 print(NUMBER) NUMBER = 125 print(NUMBER) # Variable a: int = 345 print(a) a = "Irgendwas" print(a) # Variable (Pipe-Symbol), ab Python 3.10 b : int|str = 567 print(b) b = "sonstiges" print(b) Ausgabe mit python: 123 125 345 Irgendwas 567 sonstiges Ausgabe mit mypy: test3.py:6: error: Cannot assign to final name "NUMBER" [misc] test3.py:12: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] Found 2 errors in 1 file (checked 1 source file) Python kennt übrigens keine Konstanten. Man kann diese mittels Großbuchstaben andeuten. Das ist aber nur Konvention, hat aber keine sonstigen Auswirkungen. Oder man verwendet wie oben gezeigt <code>Final</code>. Dann werfen Typchecker Errors aus. ==pydoc== pydoc ist ein Werkzeug, um Python-Module, -Funktionen und -Klassen zu dokumentieren. Beispiel <code>test1.py</code>: """Rechen-Modul. Funktionen die von pydoc gelesen werden. """ def multiply(a, b): """Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a * b def add(a, b): """Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl """ return a + b if __name__ == '__main__': print(multiply(3, 5)) print(add(4, 6)) Ausgabe mit <code>python3.13 -m pydoc test1</code> auf der Konsole: Help on module test1: NAME test1 - Rechen-Modul. DESCRIPTION Funktionen die von pydoc gelesen werden. FUNCTIONS add(a, b) Addiert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl multiply(a, b) Multipliziert zwei Zahlen und gibt das Ergebnis zurück. Argumente: a -- erste Zahl b -- zweite Zahl FILE /home/xyz/tmp/test1.py Siehe zu diesem Thema z.B. auch [https://docs.python.org/3/library/pydoc.html]. Mittels <code>python3.13 -m pydoc -w test1</code> kann man auch eine HTML-Dokumentation erstellen. Wofür <code>if __name__ == '__main__':</code> steht, siehe z.B. etwas später im Abschnitt [[Ing_Mathematik:_Python#Doctests]]. == 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.3 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.5.1 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://build123d.readthedocs.io/en/latest/ build123d] * [https://docs.vtk.org/en/latest/api/python.html VTK] * [https://docs.bokeh.org/en/latest/docs/first_steps.html bokeh] * [https://py5coding.org/ py5] == 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] * [https://pint.readthedocs.io/en/stable/ pint] = 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] * Iacono et al.: Programmieren trainieren; 3. Aufl., Hanser, 2023, ISBN 978-3-446-47766-7 * 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}} d2vapz3fd8uv078alh3263xnmybhbux Traktorenlexikon: Allis-Chalmers HD 3 0 123170 1092066 2026-10-10T20:50:46Z Baupit 56622 Neue Seite (vgl. [[WB:AZ]]) 1092066 wikitext text/x-wiki {{:Traktorenlexikon: Navigation |HERSTELLER-LINK=Traktorenlexikon: Allis-Chalmers |HERSTELLER= Allis-Chalmers}} {{:Traktorenlexikon: Modell-Infobox | HERSTELLER = ALLIS-CHALMERS | MODELLREIHE = HD-Baureihe | MODELL = HD-3 | BILD = | BILDBESCHREIBUNG = | BAUWEISE = Blockbauweise | PRODUKTIONSBEGINN = 1960 | PRODUKTIONSENDE = 1968 | STÜCKZAHL = | EIGENGEWICHT = 2.826 (H-3: 2.708) | LÄNGE = 2.760 | BREITE = 1.575 | HÖHE = 1.821 | RADSTAND = 1.626 | BODENFREIHEIT = 254 | SPURWEITE = 1.219 | SPURWEITE VORNE = | SPURWEITE HINTEN = | WENDERADIUS MIT LENKBREMSE = 3.937 | WENDERADIUS OHNE LENKBREMSE = | KETTEN = 1.619 x 356 | LEISTUNG KW = 23,5 | LEISTUNG PS = 32 | NENNDREHZAHL = 1.650 | ZYLINDER = 4 | HUBRAUM = 2.857/2.444 | DREHMOMENTANSTIEG = | KRAFTSTOFF = Diesel/Benzin | KÜHLSYSTEM = Wasserkühlung | ANTRIEBSTYP = Kettenantrieb | GETRIEBE = 4 V/4 R oder 8 V/2 R | HÖCHSTGESCHWINDIGKEIT = 8,0 | KATEGORIESORTIERUNG = Raupenschlepper }} Im Jahr 1928 stieg ALLIS-CHALMERS nach der Übernahme der MONARCH-TRACTOR-Company, in den Markt für Kettenfahrzeuge ein. Die HD-Baureihe wurde in den Nachkriegsjahren zur bekannten Produktlinie der ALLIS-CHALMERS Raupenschlepper. Der dieselbetriebene ALLIS-CHALMERS HD-3 und der benzinbetriebene ALLIS-CHALMERS H-3 waren als wendige und robuste Maschinen konzipiert, die auf nassen Böden den herkömmlichen Radtraktoren überlegen waren. Beide Ausführungen basierten in technischer Hinsicht auf dem Modell ALLIS-CHALMERS D-15. Beim Getriebe standen mit einem 4/4-Gänge SHUTTLE-Getriebe und einem 8/2-Gänge POWER-DIRECTOR-Getriebe, zwei Varianten zur Auswahl. ==Motor== * ALLIS-CHALMERS, Typ: D-175, stehender wassergekühlter Vierzylinder-Dieselmotor mit Direkteinspritzung, Druckschmierung mittels Zahnradpumpe, ALLIS-CHALMERS-Düse und Düsenhalter, zahnradgetriebene Nockenwelle, LANOVA-CELL-Brennkammer, auswechselbaren-nasse Zylinderlaufbuchsen, ROOSA-MASTER-Einspritzpumpe mit integriertem Drehzahlregler, fünffach-gelagerte Kurbelwelle, DONALDSON-Ölbadluftfilter, Fünfringkolben, ALLIS-CHALMERS-Kraftstoffpumpe und Lamellenkühler. * Bohrung = 90,47 mm, Hub = 111,13 mm * Hubraum = 2.857 cm³ * Verdichtungsverhältnis = 15,35:1 * Max. Drehmoment = 348 Nm bei 1.250 U/min. * Kolbengeschwindigkeit = 5,93 m/s * Drehzahlbereich = 600 bis 1.885 U/min. * Einspritzmenge = 47 bis 51 cm³ bei 1.000 Hüben und 600 U/min. * Max. Einspritzdruck = 138 bar * ROOSA-Master Einspritzpumpe, Typ: DB FGC-429 3-AF bzw. 26-AF * ALLIS-CHALMERS Einspritzdüse, Typ: 45.11201 "Alternativ:" * ALLIS-CHALMERS, stehender wassergekühlter Vierzylinder-Benzinmotor mit I-Zylinderkopf, Ventile im Kopf, Druckschmierung, DELCO-REMY-Verteiler, Kipphebelschmierung, ALLIS-CHALMERS-Kraftstoffpumpe, auswechselbaren-nasse Zylinderlaufbuchsen, MARVEL-SCHEBLER-Vergaser, fünffach gelagerte Kurbelwelle, DONALDSON-Ölbadluftfilter, Zentrifugal-Drehzahlregler, Vierringkolben und Lamellenkühler. * Bohrung = 88,9 mm, Hub = 98,43 mm * Hubraum = 2.444 cm³ * Verdichtungsverhältnis-Benzin = 7,5:1 * Kolbengeschwindigkeit = 5,41 m/s * Drehzahlbereich = 550 bis 1.850 U/min. * Durchflussmenge der Hauptdüse = 310 cm³ pro Minute * MARVEL-SCHEBLER Vergaser, Typ: TSX-815 oder TSX-879, TSX-912 und TSX-931 (7/8") * DELCO-REMY-Verteiler, Typ: 111.2607 ==Kupplung== * Pedal-betätigte, trockene Einscheibenkupplung, Typ: 9" Scheibendurchmesser = 228,6 mm "POWER-DIRECTOR" * Zwei Handbetätigte nasse Lamellenkupplungen ==Getriebe== * Im Ölbad laufendes, mechanischen SHUTTLE-Getriebe * Mechanisches Viergang-Schubradgetriebe * Shuttle-Modul bestehend aus zwei separaten, mechanischen Nasskupplungspaketen * Die Kraft des Motors wird über eine zentrale Welle und eine umschließende Hohlwelle geführt. * Ein zusätzliches Zahnradset sorgt innerhalb dieses Gehäuses für die Richtungsumkehr * Durch betätigen des Shuttle-Hebels wird die Kraft entweder über das Vorwärts- oder das Rückwärts-Kupplungspaket geleitet. * Mittels separaten Shuttle-Hebel wechselt man hydraulisch oder mechanisch unterstützt, zwischen Vorwärts- und Rückwärtsfahrt, ohne die Hauptkupplung zu betätigen. 4 Vorwärts- und 4 Rückwärtsgänge "Wahlweise:" * Im Ölbad laufendes, mechanisches POWER-DIRECTOR-Getriebe * Mechanisches Schubradgetriebe mit vier Vorwärts- und einem Rückwärtsgang * Power-Director-System war ein mechanisches Doppelkupplungs-System, das im Zwischengehäuse direkt hinter der Hauptkupplung sitzt * Bestehend aus einem nasslaufenden Lamellenkupplungspaket mit zwei separaten Kupplungsseiten (High und Low) * Manuell-mechanische Betätigung zwischen einer hohen Übersetzung (High), einer niedrigen Übersetzung (Low) * Lastschaltbares Umschalten zwischen High und Low, ohne Betätigung des Fußkupplungspedals 8 Vorwärts- und 2 Rückwärtsgänge ==Geschwindigkeiten vor- und rückwärts== "Geschwindigkeiten des 4/4-Gänge SHUTTLE-GETRIEBE" {| class="wikitable" |- ! bei Motordrehzahl (U/min) !! 1.650 |- | 1.Gang || 2,09 km/h |- | 2.Gang || 3,22 km/h |- | 3.Gang || 4,67 km/h |- | 4.Gang || 7,72 km/h |- ! RÜCKWÄRTSGÄNGE !! |- | 1.Gang || 2,09 km/h |- | 2.Gang || 3,22 km/h |- | 3.Gang || 4,67 km/h |- | 2.Gang || 7,72 km/h |- |} "Geschwindigkeiten des 8/2-Gänge POWER-DIRECTOR-GETRIEBE" {| class="wikitable" |- ! bei Motordrehzahl (U/min) !! 1.650 |- ! GRUPPE - LOW !! |- | 1.Gang || 1,93 km/h |- | 2.Gang || 2,57 km/h |- | 3.Gang || 3,70 km/h |- | 4.Gang || 6,12 km/h |- | 1.Rückwärtsgang || 2,90 km/h |- ! GRUPPE - HIGH !! |- | 1.Gang || 2,41 km/h |- | 2.Gang || 3,22 km/h |- | 3.Gang || 4,67 km/h |- | 2.Gang || 7,72 km/h |- | 2.Rückwärtsgang || 3,54 km/h |- |} ==Zapfwelle== * Optional mechanisch-betätigte Getriebezapfwelle * Stummel = 1 3/4"- 6 Keile * Einfach schaltbar, 540 U/min. 538 U/min. mit Nenndrehzahl * Optional Riemenscheibe mit Winkeltrieb 1.053 U/min. ==Bremsen== * Mechanisch-betätigte Bandbremse, mittels zwei separater Pedale auf die Ketten wirkend ==Achsen== * Zwei Achsen mit vier Rollen, auf Wunsch fünf Rollen Spurweite mit 14"-Kette = 1.219 mm * Vorderachse stützt sich über eine Querwelle bzw. eine gefederte Tragachse auf den beiden seitlichen Kettenrahmen ab * Starre Hinterachse mit Stirnradgetriebe und Endantrieb * Achswelle läuft in einem separaten Endantriebsgehäuse, auf der Innenseite der Welle sitzt das große Antriebszahnrad ==Lenkung== "Lenkkupplungs-Bremssystem:" * Jede Kettenseite besitzt eine eigene, separate Kupplung, die sich in einem hinteren Gehäuse des Endantriebs befindet. * Kupplungspaket entspricht wechselnd-angeordneten vier Radscheiben und drei Trennscheiben * Betätigt man einen der beiden Lenkhebel, wird die entsprechende Kupplung über ein Gestänge und Ausrücklager getrennt. * Die Kette verliert ihren Antrieb und rollt nur noch im Leerlauf mit, wodurch das Fahrzeug in eine Richtung lenkt * Für enge Kurvenfahrten oder auf der Stelle wenden, wird das System durch mechanische Bremsen ergänzt ==Hydrauliksystem und Kraftheber== * Optional Hydrauliksystem für Anbaugeräte wie Planierschilder, Frontlader oder den Heckbagger Offenes System mit Hydraulikpumpe, Typ: 2 JDS 2-L * Förderleistung = 56,8 l/min. bei 103 bar Optional mit Hydraulik-Doppelpumpe, Typ: 4 JDS 2-L * Förderleistung = 94,6 l/min. bei 103 bar "Alternativ:" Hydraulik-Doppelpumpe mit Durchflussbegrenzer, Typ: 4 JDS 3-L * Förderleistung = 94,6 l/min. bei 103 bar "Ab Seriennummer 8694:" Offenes System mit Hydraulikpumpe, Typ: 1 YDS 6-1 BL * Förderleistung = 43,5 l/min. bei 103 bar Optional mit zweistufiger Hydraulikpumpe, Typ: 1 YDS 6-OYD6 LB * Förderleistung = 43,5 l/min. und 22,7 l/min. bei 103 bar "Alternativ:" Zweistufige Hydraulikpumpe, Typ: 1 YDS-6/1 Y 1 D-6 LB * Förderleistung = 43,5 l/min. und 51,1 l/min. bei 103 bar ==Steuergeräte== * Ein einfachwirkendes Steuergerät * Optional bis zu drei Steuergeräte ==Elektrische Ausrüstung== "12 Volt-Einrichtung" * Zwei Batterie, Typ: 6 V- 135 Ah ( H-3 = 12 V-75 Ah) * DELCO-REMY Lichtmaschine, Typ: 110.0305 (12 V-25 A) * DELCO-REMY-Regler, Typ: 111.8993 (12 V-14,2 A) * ALLIS-CHALMERS Zündkerzen, Typ: 45 oder CHAMPION, Typ: J-8 ==Maße und Abmessungen== * Länge über alles = 2.761 mm * Breite über alles = 1.575 mm * Höhe über Motorhaube = 1.224 mm * Höhe über Auspuff = 1.821 mm * Radstand = 1.626 mm * Bodenfreiheit = 256 mm * Betriebsgewicht HD-3 = 3.467 kg * Betriebsgewicht H-3 = 3.354 kg ==Ketten== * Kette = 2.311 mm x 254 mm * Kettengesamtlänge = 5.182 mm * Auflagefläche der Kette = 1.619 mm * 34 integrierte Stollen je Kette = 254 mm x 51 mm "Optional:" * Kettenbreite = 178 mm, 305 mm und 356 mm ==Füllmengen== * Tankinhalt = 60,6 l * Motoröl = 3,9 l, mit Filter = 4,7 l * Kühlsystem = 10,4 l (H-3 = 9,5 l) * Getriebe = 15,1 l * Wendegetriebe = 9,9 l * Endantrieb je 6,3 l ==Verbrauch== * Kraftstoffverbrauch HD-3 = 9,7 l/h bei 33,0 DIN-PS und Nenndrehzahl * Kraftstoffverbrauch H-3 = 10,8 l/h bei 32,6 DIN-PS und Nenndrehzahl ==Kabine== * Offener Fahrerstand mit leicht gefederten Sitz, Öl- und Ladeanzeige, Temperaturanzeige, Traktormeter und flache Kotflügel ==Sonderausrüstung== ==Literatur & Weblinks== * TractorData.com * Brochure Allis Corporation * digitalcommons.unl.edu (Test-Nr. 793/61 und 794/61) <references /> {{:Traktorenlexikon: Navigation |HERSTELLER-LINK=Traktorenlexikon: Allis-Chalmers |HERSTELLER= Allis-Chalmers}} 2ebexupn7rtnxximb579sar8qmh9zk0