Wikibooks
dewikibooks
https://de.wikibooks.org/wiki/Hauptseite
MediaWiki 1.47.0-wmf.24
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
Gitarre: CAGED-Skala D-Dur-Form
0
80780
1092169
1092158
2026-10-16T14:05:13Z
Mjchael
2222
/* Übungsvorschlag */ link
1092169
wikitext
text/x-wiki
{{:Gitarre/ Navi|Sologitarre|
{{:Gitarre: Sologitarre/ Navi Improvisation in Dur und Moll}}|
img=Guitar 1.svg |bg=#F8f8f8|border=darkorange|color=dark|px=80|navi=Einführung in die Sologitarre}}
<div class="noprint>
<small>''Notitz wird nicht gedruckt!''</small>
{{Notiz|Nein, du hast dich nicht verklickt! Wir beginnen mit der D-Form, wo sich der Lagewechsel des 3. Fingersatz noch nicht mit dem Lagewechsel zwischen der G-und H-Saite aufhebt. Wir folgen im Unterricht nicht der CAGED-Reihenfolge, sondern sinnigerweise dem Quintenzirkel.}}
</div>
==Die D-Dur-Form==
{| border="1" style="border-collapse:collapse" cellpadding="3"
! Intervalle || C-Dur-Tonleiter || Subdominante || Dominante
|-
| [[Datei:Scala D-Typ.svg]] || [[Datei:C-Dur_Majorpattern_II.jpg]] || [[Image:Crd Form G.svg|150px]] || [[Image:Crd Form A Bb.svg|150px]]<br><small>Hier ist A-Form stimmiger als Bb-Form.</small>
|-
! Grundakkordform || Mollparallele || Subdominanten­ parallele || Dominanten­parallele
|-
| [[Image:Crd Form D.svg|150px]] || [[Image:Crd Form Bm7.svg|150px]] || [[Image:Crd Form Em.svg|150px]] || [[Image:Crd Form Gm7.svg|150px]]<br><small>eigentlich F#m7-Form</small>
|}
;Zugrundeliegende Dur-Tonleiter
[[Datei:Scala D-Hm.svg|200px]]
;mit ihren Hauptakkorden
[[Datei:Crd D.svg|100px]] [[Datei:Crd G.svg|100px]] [[Datei:Crd A.svg|100px]] <br>
[[Datei:Crd Hm7 x20202.svg|100px]] [[Datei:Crd Em.svg|100px]] [[Datei:Crd Fism7 2x2222.svg|100px]]
; Aufgabe:
Versuche die 6 Akkorde der Tonleiter in den Skalen zu entdecken. Sowohl die einfachen Akkorde in der D-Dur-Tonleiter, als auch die Barré-Formen in der Skala der D-Dur- bzw. Bm-Form.
=== G-Dur- bzw. E-Moll-Tonleiter ===
;in der D-Dur- bzw. Bm-Moll-Form
[[Datei:Scala D-Typ.svg|mini|CAGED-Skala der D/Bm-Form im 4. und 5. Bund.]]
Die Skala beginnt mit dem dritten Fingersatz, der in den ersten übergeht. Der zweite Fingersatz am Schluss beginnt wegen dem Lagewechsel einen Bund später. Trotz der beiden Lagewechsel ist die Skala nicht ganz so schwer, wie andere Skalen mit Lagewechsel.
<score sound="1" raw="1">
\version "2.20.0"
\header {
title = "G-Dur- bzw. E-Moll-Tonleiter"
subtitle = "in der D-Dur- bzw. Bm-Moll-Form."
subsubtitle="reine Skala"
}
Melody = {
\set Staff.midiInstrument = "electric guitar (clean)"
\repeat volta 2 {
\stemUp
\set TabStaff.minimumFret = #4
\set TabStaff.restrainOpenStrings = ##t
a,8 b, c-4 d-1 e-4 fis-1 g a |
b8 c' d' e'-1 fis' g' a' b' |
\break
c''8 b' a' g' fis' e' d'-4 c' |
b8 a g fis-1 e-4 d-1 c-4 b, | a,1 |
}
}
Gitarre = \Melody
\score {
<<
\new Voice {
\clef "treble_8" \time 4/4
\tempo 4 = 100 \key g \major
\set Score.tempoHideNote = ##t
\Gitarre
}
\new TabStaff { \Gitarre }
>>
\layout { }
}
\score {
<<
\new Voice {
\clef "treble_8" \time 4/4
\tempo 4 = 100 \key c \major
\unfoldRepeats \Gitarre
}
>>
\midi { }
}
\paper {
indent=0\mm
line-width=120\mm
oddFooterMarkup=##f
oddHeaderMarkup=##f
% bookTitleMarkup=##f
scoreTitleMarkup=##f
}
</score>
== Eine II-V-I-Verbindung ==
Die ersten Teile kennst du schon aus den Vorübungen zum 3. Fingersatz. Um das Pattern voll zu bekommen wurden noch ein paar Töne hinzugefügt.
<score sound="1" raw="1">
\version "2.20.0"
\header {
title="G-Dur in der D-Form"
subtitle="Eine II-V-I-Verbindung"
}
%% Diskant- bzw. Melodiesaiten
Diskant = \relative c {
\set TabStaff.minimumFret = #4
\set TabStaff.restrainOpenStrings = ##t
\key g \major
\repeat volta 2 {
a8^\markup { \bold {Am7} } b c d-1 e-4 fis-1 g4 |
fis8^\markup { \bold {D7} } e-4 d4-1 c8-4 b a4 |
b8^\markup { \bold {G} } c d4-1 e8-4
fis-1 g4~| g1 | \break
a8-4^\markup { \bold {Am7} } b c d-4 e-1 fis g4 |
fis8^\markup { \bold {D7} } e-1 d4-4 c8 b a4 |
b8^\markup { \bold {G} } c d4-4 e8-1 fis g4~| g8 a b c b a g4 |
% \bar "|."
}}
%% Layout- bzw. Bildausgabe
\score {
<<
\new Voice {
\clef "treble_8"
\time 4/4
\tempo 4 = 120
\set Score.tempoHideNote = ##t
\Diskant
}
\new TabStaff { \tabFullNotation \Diskant }
>>
\layout {}
}
%% Midiausgabe mit Wiederholungen, ohne Akkorde
\score {
<<
\unfoldRepeats {
\new Staff <<
\tempo 4 = 120
\time 4/4
\set Staff.midiInstrument = #"acoustic guitar (nylon)"
\clef "G_8"
\Diskant
>>
}
>>
\midi {}
}
%% unterdrückt im raw="!"-Modus das DinA4-Format.
\paper {
indent=0\mm
%% DinA4 = 210mm - 10mm Rand - 20mm Lochrand = 180mm
line-width=180\mm
oddFooterMarkup=##f
oddHeaderMarkup=##f
% bookTitleMarkup=##f
scoreTitleMarkup=##f
}
</score>
=== Quinten und Quarten ===
Quinten und Quarten bilden meist den Rahmenintervall eines Akkordes und sie werden beim Powerchord gebraucht. Nur der Tritonus (hier der Intervall zwischen C und Fis bei den Legato-Bögen) fällt etwas aus dem Rahmen.
<score sound="1" raw="1">
\version "2.20.0"
\header {
title="G-Dur in der D-Form"
subtitle="Quinten und Quarten"
}
%% Diskant- bzw. Melodiesaiten
Diskant = \relative c' {
\set TabStaff.minimumFret = #4
\set TabStaff.restrainOpenStrings = ##t
\key g \major
\repeat volta 2 {
c'8( fis,) b e, a d,
g c,( fis) b, e a,
d g, c( fis,) b e,
a d, g c,( fis) b,
e a,~ a2
a8 e' b fis'( c) g'
d a' e b' fis( c')
g d' a e' b fis'(
c) g' d a' e b'
fis( c')~ c2
% \bar "|."
}}
%% Layout- bzw. Bildausgabe
\score {
<<
\new Voice {
\clef "treble_8"
\time 3/4
\tempo 4 = 120
\set Score.tempoHideNote = ##t
\Diskant
}
\new TabStaff { \tabFullNotation \Diskant }
>>
\layout {}
}
%% Midiausgabe mit Wiederholungen, ohne Akkorde
\score {
<<
\unfoldRepeats {
\new Staff <<
\tempo 4 = 120
\time 3/4
\set Staff.midiInstrument = #"acoustic guitar (nylon)"
\clef "G_8"
\Diskant
>>
}
>>
\midi {}
}
%% unterdrückt im raw="!"-Modus das DinA4-Format.
\paper {
indent=0\mm
%% DinA4 = 210mm - 10mm Rand - 20mm Lochrand = 180mm
line-width=180\mm
oddFooterMarkup=##f
oddHeaderMarkup=##f
% bookTitleMarkup=##f
scoreTitleMarkup=##f
}
</score>
=== Zwei vor einen zurück ===
<score sound="1" raw="1">
\version "2.20.0"
\header {
title="G-Dur in der D-Form"
subtitle="Zwei vor einen zurück"
}
%% Diskant- bzw. Melodiesaiten
Diskant = \relative c' {
\set TabStaff.minimumFret = #4
\set TabStaff.restrainOpenStrings = ##t
\key g \major
\repeat volta 2 {
a,8 b c b c d c d e d e fis |
e fis g fis g a g a b a b c |
b c d c d e d e fis e fis g |
fis g a g a b a b c b c4 | \break
c8 b a b a g a g fis g fis e |
fis e d e d c d c b c b a |
b a g a g fis g fis e fis e d |
e d c d c b c b a4.
% \bar "|."
}}
%% Layout- bzw. Bildausgabe
\score {
<<
\new Voice {
\clef "treble_8"
\time 6/8
\tempo 4 = 120
\set Score.tempoHideNote = ##t
\Diskant
}
\new TabStaff { \tabFullNotation \Diskant }
>>
\layout {}
}
%% Midiausgabe mit Wiederholungen, ohne Akkorde
\score {
<<
\unfoldRepeats {
\new Staff <<
\tempo 4 = 120
\time 3/4
\set Staff.midiInstrument = #"acoustic guitar (nylon)"
\clef "G_8"
\Diskant
>>
}
>>
\midi {}
}
%% unterdrückt im raw="!"-Modus das DinA4-Format.
\paper {
indent=0\mm
%% DinA4 = 210mm - 10mm Rand - 20mm Lochrand = 180mm
line-width=180\mm
oddFooterMarkup=##f
oddHeaderMarkup=##f
% bookTitleMarkup=##f
scoreTitleMarkup=##f
}
</score>
{{Todo|Attraktivere Übungen erstellen!|Mjchael|Sologitarre}}
== Quintfallsequenz==
{{Baustelle}}
Wir wandern Septim-Akkorde in Quinten herunter.
<score sound="1" raw="1">
\version "2.20.0"
\header {
title="G-Dur in der D-Form"
subtitle="Eine II-V-I-Verbindung"
}
%% Diskant- bzw. Melodiesaiten
Diskant = {
\set TabStaff.minimumFret = #4
\set TabStaff.restrainOpenStrings = ##t
\key g \major
\repeat volta 2 {
a'8^\markup { \bold {Am7} }
g' e' c'
d'^\markup { \bold {D7} }
fis' a' c''
b'8^\markup { \bold {Gj7} }
g' fis' d'
c'^\markup { \bold {Cj7} }
e' g' b'
a'^\markup { \bold {"F#m7b5"} }
fis' e' c'
b^\markup { \bold {Bm7} }
d' fis' a'
b'^\markup { \bold {Em7} }
g' e' d'
b^\markup { \bold {E7} }
d' e' gis'
g'8^\markup { \bold {Am7} }
e' c' a
fis^\markup { \bold {D7} }
a c' d'
b8^\markup { \bold {Gj7} }
g fis d
c^\markup { \bold {Cj7} }
e g b
a^\markup { \bold {"F#m7b5"} }
fis e c
b,^\markup { \bold {Bm7} }
d fis a
g^\markup { \bold {Em7} }
e d b,
gis,^\markup { \bold {E7} }
b, d e gis
%% Am7 D7 Gj7 Cj7 - Fis°⁷ B7 Em7 E7
% \bar "|."
}}
%% Layout- bzw. Bildausgabe
\score {
<<
\new Voice {
\clef "treble_8"
\time 4/4
\tempo 4 = 120
\set Score.tempoHideNote = ##t
\Diskant
}
\new TabStaff { \tabFullNotation \Diskant }
>>
\layout {}
}
%% Midiausgabe mit Wiederholungen, ohne Akkorde
\score {
<<
\unfoldRepeats {
\new Staff <<
\tempo 4 = 120
\time 4/4
\set Staff.midiInstrument = #"acoustic guitar (nylon)"
\clef "G_8"
\Diskant
>>
}
>>
\midi {}
}
%% unterdrückt im raw="!"-Modus das DinA4-Format.
\paper {
indent=0\mm
%% DinA4 = 210mm - 10mm Rand - 20mm Lochrand = 180mm
line-width=180\mm
oddFooterMarkup=##f
oddHeaderMarkup=##f
% bookTitleMarkup=##f
scoreTitleMarkup=##f
}
</score>
== Anwendung ==
Weder die D-Form, noch die Bm7-Form dürfte für dich als verschiebbare Akkordform geläufig sein. Von daher wird die Skala wohl nicht deine erste Wahl bei Stücken in Dur oder Moll sein. Der Akkord in der D-Form geht nicht unbedingt leicht von den Fingern. Zur Orientierung ist er daher nur bedingt geeignet. Es sei denn, die D-Form würde so liegen, dass man noch eine leere Saite im Bass ausnutzen kann. Das gelingt allerdings nur in wenigen Tonarten.
Hier lohnt sich auch kein Ausflug in die Mollparallelen, denn die Moll-Parallele in der Bm7-Form wird eigentlich nur von den Jazzern angewandt.
Von daher wirst du am ehesten beim Wechseln von einer Nachbarskala (C- oder E|F-Form) in die D-Form hineinrutscht, was allerdings Stoff für eine der folgenden Lektionen sein wird.
== Übungsvorschlag ==
Verschiebe mal den D-Dur-Akkord zwei Bünde weiter. Wenn du die A- und die G-Saite auslässt (dämpfst) und nur die leere E-Saite im Bass hast, dann hast du einen E-Dur-Akkord. (E = 0xx454) Da wo der Ringfinger ist, da ist auch der Grundton (1) der E-Dur-Skala. Wandere davon die Skala abwärts.
*{{Youtube-Suche|axis+of+awesome+4+chords+with+song+titles|Awesome 4 Chords (Axis of Awesome)}}
Verschiebe mal den D-Dur-Akkord in den 7. Bund und du erhältst einen G/D (xx0787) Von dort aus arbeitest du dich jetzt rückwärts in die G-Dur-Tonleiter (D-Form), die vom 4. bis zum 8. Bund geht.
*{{Youtube-Suche|American+Pie+Don+McLean|American Pie (Don McLean)}}
* {{Youtube-Suche|Another+Four+Chords+Benny+Davis)|"Another Four Chords" von Benny Davis)}} (Millenium-Kadenz)
<noinclude>
{{Navigation zurückhochvor
|zurücklink=Gitarre: CAGED-Stammtöne - Zweiter Fingersatz
|zurücktext=Stammtöne 2. Fingersatz
|hochlink=#top
|hochtext=Hoch zum Seitenanfang
|vorlink=Gitarre: CAGED-Skala G-Dur-Form
|vortext=CAGED-Skala G-Dur-Form
}}
</noinclude>
jfu0x2eyie7fii723eok3j3j6pprj5t
1092179
1092169
2026-10-17T04:56:43Z
Mjchael
2222
/* Quintfallsequenz */
1092179
wikitext
text/x-wiki
{{:Gitarre/ Navi|Sologitarre|
{{:Gitarre: Sologitarre/ Navi Improvisation in Dur und Moll}}|
img=Guitar 1.svg |bg=#F8f8f8|border=darkorange|color=dark|px=80|navi=Einführung in die Sologitarre}}
<div class="noprint>
<small>''Notitz wird nicht gedruckt!''</small>
{{Notiz|Nein, du hast dich nicht verklickt! Wir beginnen mit der D-Form, wo sich der Lagewechsel des 3. Fingersatz noch nicht mit dem Lagewechsel zwischen der G-und H-Saite aufhebt. Wir folgen im Unterricht nicht der CAGED-Reihenfolge, sondern sinnigerweise dem Quintenzirkel.}}
</div>
==Die D-Dur-Form==
{| border="1" style="border-collapse:collapse" cellpadding="3"
! Intervalle || C-Dur-Tonleiter || Subdominante || Dominante
|-
| [[Datei:Scala D-Typ.svg]] || [[Datei:C-Dur_Majorpattern_II.jpg]] || [[Image:Crd Form G.svg|150px]] || [[Image:Crd Form A Bb.svg|150px]]<br><small>Hier ist A-Form stimmiger als Bb-Form.</small>
|-
! Grundakkordform || Mollparallele || Subdominanten­ parallele || Dominanten­parallele
|-
| [[Image:Crd Form D.svg|150px]] || [[Image:Crd Form Bm7.svg|150px]] || [[Image:Crd Form Em.svg|150px]] || [[Image:Crd Form Gm7.svg|150px]]<br><small>eigentlich F#m7-Form</small>
|}
;Zugrundeliegende Dur-Tonleiter
[[Datei:Scala D-Hm.svg|200px]]
;mit ihren Hauptakkorden
[[Datei:Crd D.svg|100px]] [[Datei:Crd G.svg|100px]] [[Datei:Crd A.svg|100px]] <br>
[[Datei:Crd Hm7 x20202.svg|100px]] [[Datei:Crd Em.svg|100px]] [[Datei:Crd Fism7 2x2222.svg|100px]]
; Aufgabe:
Versuche die 6 Akkorde der Tonleiter in den Skalen zu entdecken. Sowohl die einfachen Akkorde in der D-Dur-Tonleiter, als auch die Barré-Formen in der Skala der D-Dur- bzw. Bm-Form.
=== G-Dur- bzw. E-Moll-Tonleiter ===
;in der D-Dur- bzw. Bm-Moll-Form
[[Datei:Scala D-Typ.svg|mini|CAGED-Skala der D/Bm-Form im 4. und 5. Bund.]]
Die Skala beginnt mit dem dritten Fingersatz, der in den ersten übergeht. Der zweite Fingersatz am Schluss beginnt wegen dem Lagewechsel einen Bund später. Trotz der beiden Lagewechsel ist die Skala nicht ganz so schwer, wie andere Skalen mit Lagewechsel.
<score sound="1" raw="1">
\version "2.20.0"
\header {
title = "G-Dur- bzw. E-Moll-Tonleiter"
subtitle = "in der D-Dur- bzw. Bm-Moll-Form."
subsubtitle="reine Skala"
}
Melody = {
\set Staff.midiInstrument = "electric guitar (clean)"
\repeat volta 2 {
\stemUp
\set TabStaff.minimumFret = #4
\set TabStaff.restrainOpenStrings = ##t
a,8 b, c-4 d-1 e-4 fis-1 g a |
b8 c' d' e'-1 fis' g' a' b' |
\break
c''8 b' a' g' fis' e' d'-4 c' |
b8 a g fis-1 e-4 d-1 c-4 b, | a,1 |
}
}
Gitarre = \Melody
\score {
<<
\new Voice {
\clef "treble_8" \time 4/4
\tempo 4 = 100 \key g \major
\set Score.tempoHideNote = ##t
\Gitarre
}
\new TabStaff { \Gitarre }
>>
\layout { }
}
\score {
<<
\new Voice {
\clef "treble_8" \time 4/4
\tempo 4 = 100 \key c \major
\unfoldRepeats \Gitarre
}
>>
\midi { }
}
\paper {
indent=0\mm
line-width=120\mm
oddFooterMarkup=##f
oddHeaderMarkup=##f
% bookTitleMarkup=##f
scoreTitleMarkup=##f
}
</score>
== Eine II-V-I-Verbindung ==
Die ersten Teile kennst du schon aus den Vorübungen zum 3. Fingersatz. Um das Pattern voll zu bekommen wurden noch ein paar Töne hinzugefügt.
<score sound="1" raw="1">
\version "2.20.0"
\header {
title="G-Dur in der D-Form"
subtitle="Eine II-V-I-Verbindung"
}
%% Diskant- bzw. Melodiesaiten
Diskant = \relative c {
\set TabStaff.minimumFret = #4
\set TabStaff.restrainOpenStrings = ##t
\key g \major
\repeat volta 2 {
a8^\markup { \bold {Am7} } b c d-1 e-4 fis-1 g4 |
fis8^\markup { \bold {D7} } e-4 d4-1 c8-4 b a4 |
b8^\markup { \bold {G} } c d4-1 e8-4
fis-1 g4~| g1 | \break
a8-4^\markup { \bold {Am7} } b c d-4 e-1 fis g4 |
fis8^\markup { \bold {D7} } e-1 d4-4 c8 b a4 |
b8^\markup { \bold {G} } c d4-4 e8-1 fis g4~| g8 a b c b a g4 |
% \bar "|."
}}
%% Layout- bzw. Bildausgabe
\score {
<<
\new Voice {
\clef "treble_8"
\time 4/4
\tempo 4 = 120
\set Score.tempoHideNote = ##t
\Diskant
}
\new TabStaff { \tabFullNotation \Diskant }
>>
\layout {}
}
%% Midiausgabe mit Wiederholungen, ohne Akkorde
\score {
<<
\unfoldRepeats {
\new Staff <<
\tempo 4 = 120
\time 4/4
\set Staff.midiInstrument = #"acoustic guitar (nylon)"
\clef "G_8"
\Diskant
>>
}
>>
\midi {}
}
%% unterdrückt im raw="!"-Modus das DinA4-Format.
\paper {
indent=0\mm
%% DinA4 = 210mm - 10mm Rand - 20mm Lochrand = 180mm
line-width=180\mm
oddFooterMarkup=##f
oddHeaderMarkup=##f
% bookTitleMarkup=##f
scoreTitleMarkup=##f
}
</score>
=== Quinten und Quarten ===
Quinten und Quarten bilden meist den Rahmenintervall eines Akkordes und sie werden beim Powerchord gebraucht. Nur der Tritonus (hier der Intervall zwischen C und Fis bei den Legato-Bögen) fällt etwas aus dem Rahmen.
<score sound="1" raw="1">
\version "2.20.0"
\header {
title="G-Dur in der D-Form"
subtitle="Quinten und Quarten"
}
%% Diskant- bzw. Melodiesaiten
Diskant = \relative c' {
\set TabStaff.minimumFret = #4
\set TabStaff.restrainOpenStrings = ##t
\key g \major
\repeat volta 2 {
c'8( fis,) b e, a d,
g c,( fis) b, e a,
d g, c( fis,) b e,
a d, g c,( fis) b,
e a,~ a2
a8 e' b fis'( c) g'
d a' e b' fis( c')
g d' a e' b fis'(
c) g' d a' e b'
fis( c')~ c2
% \bar "|."
}}
%% Layout- bzw. Bildausgabe
\score {
<<
\new Voice {
\clef "treble_8"
\time 3/4
\tempo 4 = 120
\set Score.tempoHideNote = ##t
\Diskant
}
\new TabStaff { \tabFullNotation \Diskant }
>>
\layout {}
}
%% Midiausgabe mit Wiederholungen, ohne Akkorde
\score {
<<
\unfoldRepeats {
\new Staff <<
\tempo 4 = 120
\time 3/4
\set Staff.midiInstrument = #"acoustic guitar (nylon)"
\clef "G_8"
\Diskant
>>
}
>>
\midi {}
}
%% unterdrückt im raw="!"-Modus das DinA4-Format.
\paper {
indent=0\mm
%% DinA4 = 210mm - 10mm Rand - 20mm Lochrand = 180mm
line-width=180\mm
oddFooterMarkup=##f
oddHeaderMarkup=##f
% bookTitleMarkup=##f
scoreTitleMarkup=##f
}
</score>
=== Zwei vor einen zurück ===
<score sound="1" raw="1">
\version "2.20.0"
\header {
title="G-Dur in der D-Form"
subtitle="Zwei vor einen zurück"
}
%% Diskant- bzw. Melodiesaiten
Diskant = \relative c' {
\set TabStaff.minimumFret = #4
\set TabStaff.restrainOpenStrings = ##t
\key g \major
\repeat volta 2 {
a,8 b c b c d c d e d e fis |
e fis g fis g a g a b a b c |
b c d c d e d e fis e fis g |
fis g a g a b a b c b c4 | \break
c8 b a b a g a g fis g fis e |
fis e d e d c d c b c b a |
b a g a g fis g fis e fis e d |
e d c d c b c b a4.
% \bar "|."
}}
%% Layout- bzw. Bildausgabe
\score {
<<
\new Voice {
\clef "treble_8"
\time 6/8
\tempo 4 = 120
\set Score.tempoHideNote = ##t
\Diskant
}
\new TabStaff { \tabFullNotation \Diskant }
>>
\layout {}
}
%% Midiausgabe mit Wiederholungen, ohne Akkorde
\score {
<<
\unfoldRepeats {
\new Staff <<
\tempo 4 = 120
\time 3/4
\set Staff.midiInstrument = #"acoustic guitar (nylon)"
\clef "G_8"
\Diskant
>>
}
>>
\midi {}
}
%% unterdrückt im raw="!"-Modus das DinA4-Format.
\paper {
indent=0\mm
%% DinA4 = 210mm - 10mm Rand - 20mm Lochrand = 180mm
line-width=180\mm
oddFooterMarkup=##f
oddHeaderMarkup=##f
% bookTitleMarkup=##f
scoreTitleMarkup=##f
}
</score>
{{Todo|Attraktivere Übungen erstellen!|Mjchael|Sologitarre}}
== Quintfallsequenz==
{{Baustelle}}
Wir wandern Septim-Akkorde in Quinten herunter.
<score sound="1" raw="1">
\version "2.20.0"
\header {
title="G-Dur in der D-Form"
subtitle="Eine II-V-I-Verbindung"
}
%% Diskant- bzw. Melodiesaiten
Diskant = {
\set TabStaff.minimumFret = #4
\set TabStaff.restrainOpenStrings = ##t
\key g \major
\repeat volta 2 {
c'8^\markup { \bold {Am7} }
e' g' a'~2
d'8^\markup { \bold {D7} }
fis' a' c''~2
d'8^\markup { \bold {Gj7} }
fis' g' b'~2
c'8^\markup { \bold {Cj7} }
e' g' b'~2
c'8^\markup { \bold {"F#m7b5"} }
e' fis' a'~2
b8^\markup { \bold {Bm7} }
d' fis' a'~2
b8^\markup { \bold {Em7} }
d' e' g'~2
b8^\markup { \bold {E7} }
d' e' gis'~2
a8^\markup { \bold {Am7} }
c' e' g'-2
a8^\markup { \bold {D7} }
c' d' fis'~2
g8^\markup { \bold {Gj7} }
b d' fis'~2
g8^\markup { \bold {Cj7} }
b c' e'~2
fis8^\markup { \bold {"F#m7b5"} }
a c' e'~2
fis8^\markup { \bold {Bm7} }
a b d'~2
e8^\markup { \bold {Em7} }
g b d'~2
e8^\markup { \bold {E7} }
gis b d'~2
%% Am7 D7 Gj7 Cj7 - Fis°⁷ B7 Em7 E7
% \bar "|."
}}
%% Layout- bzw. Bildausgabe
\score {
<<
\new Voice {
\clef "treble_8"
\time 4/4
\tempo 4 = 120
\set Score.tempoHideNote = ##t
\Diskant
}
\new TabStaff { \tabFullNotation \Diskant }
>>
\layout {}
}
%% Midiausgabe mit Wiederholungen, ohne Akkorde
\score {
<<
\unfoldRepeats {
\new Staff <<
\tempo 4 = 120
\time 4/4
\set Staff.midiInstrument = #"acoustic guitar (nylon)"
\clef "G_8"
\Diskant
>>
}
>>
\midi {}
}
%% unterdrückt im raw="!"-Modus das DinA4-Format.
\paper {
indent=0\mm
%% DinA4 = 210mm - 10mm Rand - 20mm Lochrand = 180mm
line-width=180\mm
oddFooterMarkup=##f
oddHeaderMarkup=##f
% bookTitleMarkup=##f
scoreTitleMarkup=##f
}
</score>
== Anwendung ==
Weder die D-Form, noch die Bm7-Form dürfte für dich als verschiebbare Akkordform geläufig sein. Von daher wird die Skala wohl nicht deine erste Wahl bei Stücken in Dur oder Moll sein. Der Akkord in der D-Form geht nicht unbedingt leicht von den Fingern. Zur Orientierung ist er daher nur bedingt geeignet. Es sei denn, die D-Form würde so liegen, dass man noch eine leere Saite im Bass ausnutzen kann. Das gelingt allerdings nur in wenigen Tonarten.
Hier lohnt sich auch kein Ausflug in die Mollparallelen, denn die Moll-Parallele in der Bm7-Form wird eigentlich nur von den Jazzern angewandt.
Von daher wirst du am ehesten beim Wechseln von einer Nachbarskala (C- oder E|F-Form) in die D-Form hineinrutscht, was allerdings Stoff für eine der folgenden Lektionen sein wird.
== Übungsvorschlag ==
Verschiebe mal den D-Dur-Akkord zwei Bünde weiter. Wenn du die A- und die G-Saite auslässt (dämpfst) und nur die leere E-Saite im Bass hast, dann hast du einen E-Dur-Akkord. (E = 0xx454) Da wo der Ringfinger ist, da ist auch der Grundton (1) der E-Dur-Skala. Wandere davon die Skala abwärts.
*{{Youtube-Suche|axis+of+awesome+4+chords+with+song+titles|Awesome 4 Chords (Axis of Awesome)}}
Verschiebe mal den D-Dur-Akkord in den 7. Bund und du erhältst einen G/D (xx0787) Von dort aus arbeitest du dich jetzt rückwärts in die G-Dur-Tonleiter (D-Form), die vom 4. bis zum 8. Bund geht.
*{{Youtube-Suche|American+Pie+Don+McLean|American Pie (Don McLean)}}
* {{Youtube-Suche|Another+Four+Chords+Benny+Davis)|"Another Four Chords" von Benny Davis)}} (Millenium-Kadenz)
<noinclude>
{{Navigation zurückhochvor
|zurücklink=Gitarre: CAGED-Stammtöne - Zweiter Fingersatz
|zurücktext=Stammtöne 2. Fingersatz
|hochlink=#top
|hochtext=Hoch zum Seitenanfang
|vorlink=Gitarre: CAGED-Skala G-Dur-Form
|vortext=CAGED-Skala G-Dur-Form
}}
</noinclude>
l4rj4ffp52hna7taxn1r582pj87hsxr
Astronomie von der Frühgeschichte bis zur Neuzeit/ Mondzyklen
0
114818
1092181
1088822
2026-10-17T09:49:51Z
Bautsch
35687
/* Siderischer Monat */ versus tropischer Monat
1092181
wikitext
text/x-wiki
[[Datei:Mondfinsternis.7.9.2025.20.46MESZ.Berlin.png|mini|rechts|hochkant=2|Ein bei Vollmond während einer Mondfinsternis im Kernschatten der Erde befindlicher Blutmond nach Mondaufgang von Berlin aus gesehen am 7. September 2025.]]
== Monat ==
Die Bezeichnung '''Monat''' stammt etymologisch von unserem Erdmond ab. Es handelt sich um ein Erbwort, das auf die seit dem 8. Jahrhundert bezeugten althochdeutschen Formen ''mānōd'' beziehungsweise ''mānōth'' zurückgeht. Diese wiederum stammen vom indoeuropäischen Wort ''mēnōt'' ab, das sowohl ''Monat'' als auch ''Mond'' bedeuten kann.<ref>[https://indogermanisch.org/pokorny-etymologisches-woerterbuch/m%C4%93n%C5%8Dt_gen_m%C4%93neses_woraus_m%C4%93nes-_m%C4%93ns-_m%C4%93s-_m%C4%93n.htm mēnōt], Pokorny - Indogermanisches etymologisches Wörterbuch</ref>
== Die Naturgeschichte von Plinius dem Älteren ==
'''Gaius Plinius Secundus''' ("der Ältere") hat im ersten nachchristlichen Jahrhundert über den '''Mond''' geschrieben. Das neunte Kapitel des zweiten Buches aus dem ersten Band hat den Titel ''Von der Natur des Mondes''. Über den Mond schreibt Plinius Folgendes:<ref name="Denso1764">Plinius: ''Naturgeschichte'', erster Band, zweites Buch, neuntes Kapitel: ''Von der Natur des Mondes'', übersetzt von Johann Daniel Denso (1708–1795), Rostock und Greifswald, Anton Ferdinand Rösens Buchhandlung, 1764, in moderne deutsche Sprache gesetzt.</ref>
<blockquote>
Aber alle diese [Wandelgestirne] übertrifft an Bewunderungswürdigkeit das Gestirn des '''Mondes''', das niedrigste, der Erde bekannteste, und zur Verminderung der Finsternis erfunden. Dieser hat durch seinen Wandel und veränderte Gestalt, da er beständig zu- und abnimmt, den Witz seiner Beobachter gequält, die es sich für einen Schimpf hielten, wenn man das nächste Gestirn nicht kennen sollte. Ja er ist auch in Spitzen gekrümmt, bald gerade halbgeteilt, bald in eine ganze Scheibe gebogen, bald, da er einem dunkeln Flecken ähnlich war, scheint er plötzlich hervor, und ist ist er ohne Grenzen rund und voll, ist gar nicht zu sehen. Bald scheint er die ganze Nacht durch, bald geht er spåt auf, und verstärkt noch bei Tage den Schein der Sonne. Er wird verfinstert, und lässt sich auch in der Verfinsterung noch sehen. Beim Ausgang des Monats sieht man ihn nicht, und man glaubt nicht, dass er alsdenn verfinstert werde.<br/>
Bald steht er niedrig, bald erhaben, und auch dies nicht auf eine Art. Bald steht er in der Höhe des Himmels, bald reicht er an die Berge: jetzt steht er hoch gegen Norden, bald tief gegen Süden herunter. Alles dies hat '''Endymion''' unter allen Menschen an ihm zuerst wahrgenommen, und deswegen ist die Sage, dass dieser in ihm verliebt gewesen sei. Wir sind wahrlich gegen diejenigen, welche uns zuerst bei diesem Himmelslicht durch ihre Arbeit und Sorge ein Licht aufgesteckt haben, nicht dankbar. Es ist ein wunderbares Verderben des menschlichen Witzes, dass wir ein Vergnügen daran finden, Blutvergießen und Mord in unsern Jahrbüchern aufzuzeichnen, damit die, welche die Welt nicht kennen, doch von unsern Lastern wissen mochten.<br/>
Doch aber da der Mond der Weltachse am nächsten steht, und also den engsten Umlauf hat, so durchlauft er in 27 und 1/3 Tag eben den Raum, welchen der höchste Stern des Saturns, wie gesagt ist, in dreißig Jahren zurücklegt. Hiernächst hält er sich in der Zusammenkunft mit der Sonne zwei Tage, wenn es am langsamsten geschieht, aus, und geht mit dem dreißigsten Tag zu seinen vorigen Abwechselungen wieder hervor. Ich weiß nicht, ob er nicht unter allen am Himmel der einzige Lehrmeister gewesen sei, dass man das Jahr in einen Raum von zwölf Monaten einteilen müsse, denn ebenso oft folgt er der Sonne, wenn sie zu einem neuen Zeichen zurückkehrt.<br/>
Er wird, wie die übrigen Sterne vom Sonnenlicht verdeckt. Denn wir sehen, dass er von derselben sein Licht gänzlich entlehne, und damit dergestalt leuchte, als wenn wir ihr zurückprallendes Licht in einem Wasser schweben sehen. Da er die Feuchtigkeit mit sanfterer und unvollkommener Kraft auflöst, auch wohl vermehrt, welche die Strahlen der Sonne verzehren. Hierher kommt es auch, dass man ihn in ungleichem Licht sieht, dass er alsdenn nur voll ist, wenn er der Sonne gerade gegenübersteht, in den übrigen Tagen nur so viel desselben, als er von jener empfängt, auf seiner Scheibe zeigt. Wenn er mit der Sonne zusammenkommt, kann man ihn nicht sehen, weil er uns die dunkle Seite zukehrt, und alles Geschöpfte dorthin, woher er es empfangen hat, wieder zurückwirft.<br/>
Es scheint außer Zweifel gesetzt zu sein, dass die Gestirne ihre Nahrung von der Feuchtigkeit der Erde haben. Denn er wird auf der einen Hälfte seiner Scheibe oft voller Flecken gesehen, weil er etwa noch nicht Kraft genug hat, mehr als ihm dienlich ist, an sich zu ziehen. Seine Flecken sind gewiss nichts anderes, als die mit der Feuchtigkeit von der Erde mit fortgerissenen Unreinigkeiten.<br/>
</blockquote>
<div style="clear:both"></div>
== Mondperioden ==
Die zu beobachtende scheinbare Mondbahn kann im Verlauf verschiedener Perioden durch zahlreiche '''Mondzyklen''' beschrieben werden. Die kürzesten Zyklen dauern ungefähr einen Monat im Sonnenkalender, sie längeren Mondzyklen können aber auch mehrere Jahre umfassen.
Der Mond hat ähnlich wie die Sonne einen scheinbaren Winkeldurchmesser von ungefähr 30 Bogenminuten beziehungsweise 0,5 Bogengrad. Dies entspricht bei Betrachtung des eigenen Fingers mit ausgestrecktem Arm in etwa einem Viertel der Fingerdicke.
===Synodischer Monat===
[[Datei:Courtenay.Mondsichel.P1033938.png|mini|Altlicht beim Morgenletzt des Mondes (heliakischer Aufgang) über dem östlichen Horizont auf Vancouver Island kurz nach der Tag-und-Nachtgleiche im September 2019.]]
[[Datei:Neulicht.37Tauri.P1138734.jpg|mini|Neulicht beim Abenderst des Mondes (akronychischer Untergang) über dem westlichen Horizont in Berlin Anfang Mai 2022.]]
Der synodische Monat ist durch den Verlauf der Elongation des Mondes in Bezug zur Sonne beschrieben.
Der Mond umrundet die Erde ungefähr zwölfmal schneller als die Erde die Sonne und benötigt für einen Umlauf einen Monat. Die einfachste Wahrnehmung des Mondlaufs ergibt sich durch die Beobachtung der Mondphasen beziehungsweise der Elongationen des Mondes. Der '''synodische Monat''' (altgriechisch ''σύνοδος'' (''synodos'') = ''Zusammentreffen'') beschreibt die Dauer zwischen zwei gleichen Mondphasen, also von Neumond zu Neumond beziehungsweise von Vollmond zu Vollmond. Hier wird gemeinhin das Zusammentreffen von Neumond und Sonne am Himmel als Referenzzeitpunkt betrachtet. Ein synodischer Monat dauert etwa 29,53 Tage, und zwölf synodische Monate dauern demzufolge rund 354,37 Tage - das sind gut fünfeinhalb Tage weniger als 360. Dieser Zyklus ist dies Basis für die gängigen Mondkalender (Lunarkalender) mit der gegenüber dem am Sonnenjahr orientierten Solarkalender um zirka 11 Tagen kürzeren Jahreslänge. Bei Lunisolarkalendern wird durchschnittlich alle drei Jahre ein dreizehnter synodischer Monat eingeschaltet, damit der Frühlingspunkt der Sonne ungefähr in der gleichen Jahreszeit bleibt.
→ Zur Zahl Zwölf siehe auch [[Quadriviale Kuriositäten/ Zahlen#Zur Zwölf|Exkurs '''Zur Zwölf''']].
[[Datei:Horusauge.Faltung.png|mini|rechts|hochkant=2|Das altägyptische Horusauge als Folge von Rechtecken mit jeweils der Hälfte der Fläche des Vorgängers in einem Rechteck mit dem Flächeninhalt eins.]]
[[Datei:Dendera_Deckenrelief_03.JPG|mini|hochkant=2|Deckenrelief im altägyptischen Tempel von Dendera mit der Darstellung von 15 Mondphasen von links (Neumond) nach rechts (Vollmond) mit den Göttern Junit, Sopdet-Tjenenet, Hor-Behdeti, Hathor, Nephthys, Harsiese, Isis, Osiris, Nut, Geb, Tefnut, Schu, Atum und Month. Im Vollmond vor dem Gott des Mondes Thot ist das von ihm geheilte linke Auge („Mondauge“) des Lichtgottes Horus dargestellt.]]
Die Ägypter haben ihre Kalender nicht mit einer Siebentagewoche, sondern sowohl mit einer Fünftagewoche als auch mit einer Zehntagewoche geführt. Damit besteht ein synodischer Monat auf weniger als einen halben Tag genau aus sechs Fünftagewochen beziehungsweise aus drei Zehntagewochen. Es wird in der Literatur manchmal darauf hingewiesen, dass das Verhältnis der Länge eines synodischen Monats zu dreißig vollen Tagen
:<math>\frac {29,530589 \text{d}} {30 \text{d}} = 0,984353</math>
fast identisch mit dem folgenden Verhältnis ist (siehe auch Horusauge und Heqat in der altägyptischen Geschichte<ref>Donald Frazer: ''Hieroglyphs and Arithmetic of the Ancient Egyptian Scribes'', Kapitel 2.6.5 ''Hekat Fractions and Ro'', Xlibris Corporation, 2012, ISBN 9781469136462</ref>):
:<math>\frac {1} {2} + \frac {1} {4} + \frac {1} {8} + \frac {1} {16} + \frac {1} {32} + \frac {1} {64}</math>
:<math>= \frac {32} {64} + \frac {16} {64} + \frac {8} {64} + \frac {4} {64} + \frac {2} {64} + \frac {1} {64}</math>
:<math>= \frac {63} {64} = \frac {2^6 - 1} {2^6} = 1 - 2^{-6} = 0,984375</math>
Die Abweichung der beiden Verhältnisse beträgt nur 0,022 Promille. Erst nach rund 44700 Monaten oder 3700 Jahren hat sich diese Abweichung auf einen Tag aufsummiert.
Die verschiedenen Mondphasen waren für die Menschen schon immer sichtbar und konnten im Laufe eines synodischen Monats verfolgt werden. Es wird davon ausgegangen, dass zum Beispiel auch auf der Himmelsscheibe von Nebra mindestens eine Mondsichel dargestellt ist, eventuell auch der Vollmond und nach dem österreichischen Ur- und Frühgeschichtler '''Paul Gleirscher''' zusätzlich das Altlicht des Mondes:<ref>Paul Gleirscher: [https://dialnet.unirioja.es/servlet/articulo?codigo=4142096 Zum Bildprogramm der Himmelsscheibe von Nebra: Schiff oder Sichel?], Germania: Anzeiger der Römisch-Germanischen Kommission des Deutschen Archäologischen Instituts, Band 85, Nummer 1, ISSN 0016-8874, Seiten 23 bis 33, 2007</ref>
<gallery caption="Verschiedene möglicherweise auf der Himmelsscheibe von Nebra dargestellte Mondphasen" perrow="2" widths="300" heights="300">
Vollmond.P1080516.jpg|Ein im Dezember um Mitternacht fast im Zenit stehender, sehr heller Vollmond.
Zunehmende.Mondsichel.png|Ein zunehmender Mond drei Tage nach Neumond beim akronychischen Untergang am westlichen Abendhimmel drei Wochen vor der Tag-und-Nacht-Gleiche im Herbst. Die rötliche Färbung entstand genauso wie bei der untergehenden Sonne durch die Rayleigh-Streuung in der Erdatmosphäre.
Altlicht.3.Nov.2021.P1116624.jpg|Das Altlicht eines abnehmenden Mondes (Morgenletzt, vier Prozent beleuchtet) beim heliakischen Aufgang am südöstlichen Morgenhimmel der nördlichen Hemisphäre während der bürgerlichen Dämmerung einen Monat nach der Tag-und-Nacht-Gleiche im Herbst mit Erdschein.
Nebra_Scheibe_Modell.jpg|Vollständig rekonstruiertes Modell der bronzenen und mit Gold tauschierten Himmelsscheibe von Nebra.
</gallery>
===Siderischer Monat===
Der '''siderische Monat''' ist durch den Verlauf der ekliptikalen Länge des Mondes in Bezug zum Frühlingspunkt einer astronomischen Epoche definiert und beschreibt die Zeitspanne, nach der der Mond die gleiche ekliptikale Länge erreicht hat. Da sich er Frühlingspunkt gegenüber dem Fixsternhimmel entlang der Ekliptiklinie verschiebt, ist der siderische Monat vom '''tropischen Monat''' zu unterscheiden. Letzterer bezieht sich ebenfalls auf den Frühlingspunkt des aktuellen Datums, also nicht den, der zu einem bereits vergangenen Referenzzeitpunkt des Datums einer bestimmten astronomischen Epoche gegolten hat. Zur Zeit wird hierfür meist die Julianische Standardepoche J2000.0 verwendet, die sich auf die Uhrzeit 11:58:55,8 UTC am 1. Januar 2000 um bezieht. Bezogen auf diese Standardepoche dauert der tropische Monat 27,32158 Tage. Durch die langsame gegenläufige Wanderung des Frühlingspunkts besteht ein kleiner Unterschied zwischen tropischem und siderischem Monat, der mit 27,32166 Tage geringfügig länger ist. Die Differenz von 0,00008 Tagen entspricht einer Abweichung von von lediglich sieben Sekunden. Dies bedeutet, dass der Mond den aktuellen Frühlingspunkt nach einer Umrundung der Erde sieben Sekunden früher erreicht als einen Stern (lateinisch ''sideris'' = ''des Sterns'') der sich am Anfang der Umrundung noch beim alten Frühlingspunkt befunden hatte.
Es kann auch die Zeitspanne betrachtet werden, in der der Mond in Bezug auf den Fixsternhimmel entlang der Ekliptik wieder an einer beliebigen gleichen Stelle erscheint. Dies ist also auch die Dauer zwischen zwei aufeinanderfolgenden Erscheinungen des Mondes im '''Goldenen Tor der Ekliptik''', da dessen Lage durch Sterne des Fixsternhimmels bei einer bestimmten ekliptikalen Länge bestimmt ist.
→ Siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Das_Goldene_Tor_der_Ekliptik|Exkurs '''Das Goldene Tor der Ekliptik''']].
Die Einteilung der 360 Bogengrad langen Ekliptik in 28 gleiche Teile ist in der Bronzezeit verbreitet gewesen. Daraus ergibt sich ein grobes Koordinatenraster für die ekliptikale Länge des Mondes.
Auf der '''Stachelscheibe von Platt''' aus der Bronzezeit (um 1500 vor Christus) werden die 28 Mondorte der Tage eines Monats beispielsweise durch eine Kreisreihe dargestellt. Die Hohlform diente zur Herstellung von Schmuckscheiben und hat insgesamt sieben konzentrische Kreise. Davon bestehen zwei aus 12 (innen) beziehungsweise aus 28 (außen) gleichmäßig verteilten Mulden.<ref>Irene Hager und Stefan Borovits (Wien, Österreich): ''Der Vorläufer einer Oktaëteris auf dem Kalenderstein bei Leodagger/Pulkau?'', Kapitel 26.2.2 ''Astronomisch/kalendarische "Zählmaschinen" aus der Bronzezeit'', in: Gudrun Wolfschmidt (Herausgeberin): ''Orientierung, Navigation und Zeitbestimmung - Wie der Himmel den Lebensraum des Menschen prägt'', Proceedings der Tagung der Gesellschaft für Archäoastronomie in Hamburg 2017, Band 42 von Nuncius Hamburgensis - Beiträge zur Geschichte der Naturwissenschaften, Verlag tredition, 2019, ISBN 9783749767717</ref> Die zwölf inneren Mulden entsprechen entlang der Ekliptik den 12 Sonnenorten (Monaten) in einem tropischen Jahr beziehungsweise den 12 Jupiterorten (Jahren) in zwölf Jahren. Die 28 äußeren Mulden entsprechen entlang der Ekliptik den 28 Mondorten (respektive Mondhäusern beziehungsweise Mondstationen) und somit den Tagen in einem siderischen Monat. Der große kreisförmige Stachel im Zentrum der Scheibe könnte als Symbol für die Sonne stehen. Auf ihm konnte die Scheibe von unten zentrisch und drehbar gelagert werden. Mit der Scheibe konnte (abgesehen von den erforderlichen siderischen Schaltmonaten) zwölf Jahre lang in täglich wechselnden Kombinationen in den beiden Lochreihen die Lagen von Mond und Jupiter abgelesen und markiert werden. Damit konnte nach einer Einmessung der Ost-West-Richtung zum Beispiel bei der Tag-und-Nacht-Gleiche zur Jupiterrichtung im Frühlingspunkt der gesamte Lebewesenkreis (Zodiak) jederzeit mit dem täglich ein Mondhaus weiterwandernden Mond vollständig bestimmt werden, auch ohne dass der Jupiter sichtbar sein musste. Mit dieser Information ist es dann auch ohne weiteres möglich, das nicht sichtbare Lebewesenzeichen zu bestimmen, in welchem die Sonne sich aufhält.
Im der indischen Astronomie wurden zu diesem Zweck spätestens 500 Jahre danach die '''27 Mondhäuser''' (oder Mondstationen) eingeführt. Da sich die siderische und die synodische Periode um gut zwei Tage unterscheiden, liegen aufeinanderfolgende Neumonde oder Vollmonde in verschiedenen Mondhäusern, nach denen im hinduistischen Lunisolarkalender die Monate benannt werden.
Dieses System wurde etwas später von den Arabern mit '''28 Mondhäusern''' modifiziert. Das '''erste Mondhaus''' liegt bei beiden Einteilungen im Frühlingspunkt in der Epoche um Christi Geburt im '''Kopf des Lammes''' beziehungsweise des Widders (Aries) bei den nördlich der Ekliptik liegenden Sternen Scheratan und Hamal (indisch ''Ashvini'' = ''die beiden Rosseschirrenden'' und arabisch ''aš-šaraṭān'' = ''Die beiden Zeichen''). Für das '''zweite Mondhaus''' folgt der '''Bauch des Lammes''' (indisch ''Bharani'' = ''der Wegtragende'' und arabisch ''al-buṭayn'' = ''das Bäuchlein''). Die Plejaden (indisch ''Krittika'' und arabisch ''aṯ-ṯurayyā'') im fetten '''Schwanz des Lammes''' markieren im Anschluss das '''dritte Mondhaus'''. Das '''vierte Mondhaus''' ist durch den roten Riesenstern Aldebaran (arabisch ''al-dabarān'' = ''der Nachfolgende'', indisch ''Rohini'' = ''der Rötliche'') im Sternbild Stier (Taurus) gekennzeichnet.
<gallery caption="Mondstationen" mode="packed" widths="600" heights="600">
Mondhaeuser.Ekliptik.zirkular.png|Die in eine ringförmige Darstellung projizierten 28 Mondhäuser (von 1 bis 28 entgegen dem Uhrzeigersinn) mit den wichtigsten Sternen entlang der Ekliptik (rote gestrichelte Linie '''zur Epoche J0000.0'''). Der Beobachter befindet sich auf der Erde im Zentrum der Darstellung. Nach innen werden die südlichen und nach außen die nördlichen ekliptikalen Breiten gemessen. Die scheinbare Mondbahn pendelt zwischen den beiden zur Ekliptikline benachbarten Hilfslinien. Der Abstand der Hilfslinien beträgt senkrecht zur Ekliptiklinie immer fünf Bogengrad und entlang der Ekliptiklinie immer knapp dreizehn Bogengrad. Mit bis zum Nordpol zunehmender geographischer Breite des Beobachtungspunktes können auch noch knapp ein Bogengrad südlichere ekliptikale Breiten von der Mondscheibe erreicht werden, am Südpol auch noch entsprechend nördlichere ekliptikale Breiten.
Stachelscheibe_Model_zweiseitig.jpg|Die in Niederösterreich gefundene und aus Sandstein gefertigte Gussform für die '''Stachelscheibe von Platt'''.<br/>Von innen nach außen gibt es '''sieben''' konzentrische Kreise, die folgendermaßen zugeordnet werden können:<br/>- Eine große zentrische Bohrung (im Gussteil eine große stachelartige Erhebung für die '''Sonne''').<br/>- Zwölf gleichmäßig verteilte kleine Bohrungen (Zodiak mit '''zwölf''' Sternzeichen sowie für die Umlaufzeit des Planeten '''Jupiter '''in Jahren).<br/>- Drei äquidistante Kreislinien (die drei inneren Planeten '''Merkur''', '''Venus''' und '''Mars''').<br/>- Achtundzwanzig gleichmäßig verteilte kleine Bohrungen ('''Mond'''häuser).<br/>- Ein großer abschließender Kreis ('''Saturn''' als der Langsame und Beständige).
Stonehenge_phase_one.jpg|Die älteste belegte kreisförmige Struktur in Stonehenge 1 (zirka 3100 bis 2900 vor Christi) besteht aus den 56 Aubrey-Löchern (in der Abbildung weiße Kreise). Diese können unter Verwendung von Quadranten, die geographisch durch die vier Himmelsrichtungen in jeweils 14 Mondstationen geteilt sind, dazu verwendet worden sein, die ekliptikale Länge des Mondes in Bezug zum Frühlingspunkt oder zum Herbstpunkt beziehungsweise in Bezug zur Jupiterposition täglich zu markieren (der Mond erreicht den Jupiter ungefähr alle 27,5 Tage). In dieser Zählung wären alle 28 Mondhäuser halbiert, in eine Tagesstation und eine Nachtstation.
</gallery>
Zwischen dem dritten und vierten Mondhaus liegt das Goldene Tor der Ekliptik, wo der Frühlingspunkt zu Beginn der maltesischen Tarxien-Phase lag. Man beachte die fehlenden helleren ekliptiknahen Sterne im '''Trichter der Thuraya''' westlich davon, also rechts der Plejaden (ekliptikale Länge ungefähr 32 Bogengrad) bis hin zum Stern Hydor heutigen Sternbild Wassermann (Aquarius, ekliptikale Länge ungefähr 314 Bogengrad). Die hellsten ekliptiknahen Sterne in diesem Gebiet des Sternenhimmels Alpherg im Sternbild Fische (Pisces) sowie Hydor und Ancha im Sternbild Wassermann (Aquarius) erreichen lediglich die vierte Größenklasse (4<sup>m</sup>), so dass zwischen dem auffälligen offenen Sternhaufen der Plejaden und Deneb Algedi, dem hellsten Stern im Sternbild Steinbock (Capricornus), auf einer Länge von 90 Bogengrad keine hellen ekliptiknahen Sterne vorhanden sind.
→ Zur Einteilung der Ekliptik nach den monatlichen Mondstationen siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Konjunktionen#Mondhäuser|Exkurs '''Mondhäuser''']]
→ Zum dunklen Himmelsquadranten entlang der Ekliptik siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Der_Trichter_der_Thuraya|Exkurs '''Der Trichter der Thuraya''']]
===Drakonitischer Monat===
[[Datei:Drakonitischer.Monat.png|rechts|mini|hochkant=2|Schematische Darstellung der Mondbahn (gelb) im Laufe eines drakonitischen Monats in Bezug auf die Ekliptiklinie (rot). Nach dem Erreichen der südlichsten Lage in Bezug zur Ekliptiklinie wird die Mondbahn aufsteigend, und von der nördlichsten Lage in Bezug auf die Ekliptiklinie wird die Mondbahn dann wieder absteigend. In der deutschsprachigen Schweiz gibt es für diese im Laufe eines drakonitischen Monats täglich mehr oder weniger deutlich wahrnehmbaren Änderungen der ekliptikalen Breite sogar eigene Adjektive. Das Ansteigen der ekliptikalen Breite des Mondes nach Norden wird '''obsigend''' und das Abfallen des Mondes nach Süden '''nidsigend''' genannt. Direkt auf der Ekliptik befinden sich der aufsteigende und der absteigende Knoten der Mondbahn.]]
Der drakonitische Monat ist durch den Verlauf der ekliptikalen Breite des Mondes in Bezug zur Ekliptiklinie beschrieben.
Deswegen gibt es noch den '''drakonitischen Monat''' (altgriechisch ''δράκων'' (''drakon'') beziehungsweise lateinisch ''draco'' = ''Drache''), der eine Dauer von 27,212 Tagen hat. Diese Dauer beschreibt die Zeitpunkte, an denen die um gut 5 Bogengrad zur Ekliptik geneigte Mondbahn die Ekliptik kreuzt; die ekliptikale Breite des Mondes ist dann exakt null. Diese Schnittpunkte werden Mondknoten genannt und werden einmal im Monat im aufsteigenden Mondknoten und einmal im absteigenden Mondknoten erreicht. Befindet sich der Mond auf der Ekliptik, also in der Nähe dieser Mondknoten, kommt es bei dessen Sonnennähe (wenn der Neumond also in Konjunktion mit der Sonne steht) zu einer Sonnenfinsternis und bei dessen Sonnenferne (wenn der Vollmond also in Opposition zur Sonne steht) zu einer Mondfinsternis. Diese Mondpunkte wurden früher als Drachenpunkte bezeichnet, was sich aus der Vorstellung ableitete, dass ein Drache bei einer Mondfinsternis den Mond beziehungsweise bei einer Sonnenfinsternis die Sonne verschlingen würde.
Mit dem folgenden Java-Programm können die ekliptikalen Koordinaten der Sonne und des Mondes für jeden beliebigen Zeitpunkt eines Julianischen Datums in Julianischen Jahrhunderten in Bezug auf die astronomische Standardepoche J2000 berechnet werden:
'''[[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/ EkliptikaleKoordinatenMondSonne|→ Java-Programm "EkliptikaleKoordinatenMondSonne"]]'''<ref>Unter Verwendung der Formeln aus: Oliver Montenbruck, Thomas Pfleger: ''Astronomie mit dem Personal Computer'', Springer-Verlag Berlin Heidelberg GmbH, 1989, ISBN 978-3-662-05865-7</ref>
[[Datei:Ekliptikale.Breiten.Mond.siderischer.Monat.png|links|mini|hochkant=4|Die ekliptikalen Breiten des Mondes im Verlauf eines drakonitischen Monats beziehungsweise eines nur gut zweieinhalb Stunden längeren siderischen Monats mit gut 27 Tagen.]]
{| class="wikitable"
|+ Die täglichen Änderungen der ekliptikalen Breite des Mondes in Bogengrad innerhalb eines Mondviertels
!title="Tage nach aufsteigendem Knoten"| Tage nach<br/>aufsteigendem<br/>Knoten
!title="Änderung der ekliptikalen Breite"| Änderung der<br/>ekliptikalen Breite<br/>zum Vortag
|-
| 1 || 1,2°
|-
| 2 || 1,1°
|-
| 3 || 1,0°
|-
| 4 || 0,9°
|-
| 5 || 0,6°
|-
| 6 || 0,3°
|-
| 7 || 0,0°
|}
<div style="clear:both"></div>
Der Mond kann auf seiner Bahn im Laufe der Zeiten alle ekliptiknahen Himmelsobjekte inklusive aller Planeten und der Sonne bedecken und innerhalb einer Stunde wieder freigeben, die sich in einem Band bis zu gut ±5 Bogengrad nördlich oder südlich neben der Ekliptiklinie befinden.
'''{{w|Aristoteles}}''' (384 bis 322) hat dies in seiner Schrift '''"Über den Himmel"''' (altgriechisch: ''Περὶ οὐρανοῦ'' / ''Peri uranu'') anhand der von ihm beobachteten '''Bedeckung des Planeten Mars durch den zunehmenden Halbmond in der Nähe des Sterns Regulus''' (α Leonis) beschrieben und darauf hingewiesen, dass die Babylonier und die Ägypter solche Phänomene über lange Zeit beobachtet und dokumentiert hatten.<ref>Aristoteles: [http://classics.mit.edu/Aristotle/heavens.2.ii.html On the Heavens], Teil 12, Buch II, um 350 vor Christus, ins Englische übersetzt von John Leofric Stocks (* 1882; † 1937)</ref>
→ Siehe [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Konjunktionen#Aristotelische Mars-Konjunktion|Abschnitt '''Aristotelische Mars-Konjunktion''']].
Der ekliptiknahe Hauptstern Pollux im Sternbild Zwillinge (Gemini) hat sich aufgrund seiner Eigenbewegung im Laufe der letzten zehntausend Jahre so weit von der Ekliptiklinie entfernt, dass er inzwischen nicht mehr vom Mond bedeckt werden kann.
→ Für helle ekliptiknahe Objekte siehe [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Der_Fixsternhimmel#Die_sieben_hellsten_Objekte_der_Ekliptik|Abschnitt '''Die sieben hellsten Objekte der Ekliptik''']].
===Siderische Mondperioden===
Der Mond erscheint innerhalb eines tropischen Jahres dreizehn- oder vierzehnmal an einer bestimmten Stelle des Fixsternhimmels, wobei er wegen der unterschiedlichen Periodendauern von siderischen und synodischen Monaten immer ein anderes '''Mondalter''' (die Anzahl der Tage seit dem letzten Neumond) und wegen der unterschiedlichen Periodendauern von siderischen und drakonitischen Monaten immer eine andere ekliptikale Breite aufweist.
Die beiden folgenden Diagramme sollen den zeitlichen Verlauf der Mondphasen und der ekliptikalen Breiten des Mondes bei seinem Erscheinen im Goldenen Tor der Ekliptik während 254 aufeinanderfolgender siderischer Perioden mit jeweils 27,322 Tagen (insgesamt 6940 Tage beziehungsweise 19 Jahre) veranschaulichen:
[[Datei:Mondphasen.siderische.Perioden.png|links|mini|hochkant=4|Die Mondphasen bei aufeinanderfolgenden Erscheinungen des Mondes im Goldenen Tor der Ekliptik innerhalb von 254 siderischen Perioden (insgesamt 19 Jahre). Da der synodische Monat (von Neumond zu Neumond) über zwei Tage länger ist als der siderische Monat, kommt es hierbei zu Verschiebungen, weil der Mond nach Ablauf eines siderischen Monats beim Erreichen derselben ekliptikalen Länge noch nicht ganz wieder sein maximales Mondalter erreicht hat.<br/><br/>In der oberen Hälfte des Diagramms sind zunehmende und in der unteren Hälfte abnehmende Monde zu beobachten. Eine Mondphase von 0 Prozent steht für einen Neumond und eine Mondphase von ±100 Prozent für einen Vollmond.<br/><br/>Der Startpunkt (Tag 0 im Monat 0) kann zum Beispiel mit dem 22. Mai 2020 am Abend (UTC) angesetzt werden, an dem der Neumond zusammen mit der Sonne im Goldenen Tor der Ekliptik stand. Dies geschieht dann nach 19 Jahren am 23. Mai 2039 kurz nach Mitternacht (UTC) erneut.]]
[[Datei:Ekliptikale.Breiten.Mond.siderische.Perioden.png|links|mini|hochkant=4|Die ekliptikalen Breiten des Mondes bei aufeinanderfolgenden Erscheinungen des Mondes im Goldenen Tor der Ekliptik innerhalb von 254 aufeinanderfolgenden siderischen Perioden (insgesamt 19 Jahre). Da der drakonitische Monat (von einem aufsteigendem Mondknoten bis zum nächsten aufsteigenden Mondknoten) gut zweieinhalb Stunden kürzer ist als der siderische Monat, kommt es hierbei zu Verschiebungen, weil der Mond nach Ablauf eines siderischen Monats den aufsteigenden Knoten bereits wieder hinter sich gelassen hat.<br/><br/>Bei großen ekliptikalen Breiten (oben) kommt es im Goldenen Tor der Ekliptik zu Bedeckungen der Plejaden und bei kleinen ekliptikalen Breiten (unten) kommt es zu Bedeckungen der Hyaden oder des Sterns Aldebaran durch die Mondscheibe.<br/><br/>Der Startpunkt (Tag 0 im Monat 0) kann beispielsweise ebenfalls mit dem 22. Mai 2020 angesetzt werden, an dem der Neumond vom Erdmittelpunkt aus gesehen bei einer ekliptikalen Breite von zirka -2,5 Bogengrad unterhalb der Sonne, deren ekliptikale Breite definitionsgemäß 0 Bogengrad beträgt, im Goldenen Tor der Ekliptik stand.<br/><br/>Nach 18,61 Jahren (beziehungsweise 6793,5 Tagen oder gut 230 synodischen Monaten, in dieser Abbildung also nach gut 248,6 siderischen Monaten) erreicht der Mond dieselbe ekliptikale Breite und fast die gleiche Mondphase, befindet sich dann allerdings bei einer anderen ekliptikalen Länge.<br/><br/>Die kurzperiodische kleine Wellenbewegung kommt durch die Nutation der Erdachse im Bezug zur Ekliptik beziehungsweise zum Fixsternhimmel zustande; sie hat eine Periodendauer von 35 Tagen und überlagert sich mit den zirka eine Woche kürzeren siderischen Mondperiode.]]
<div style="clear:both"></div>
===Der Meton-Zyklus===
Nicht nur die Bestimmung und Vorhersage der Auf- und Untergänge der Venus haben die Aufmerksamkeit der Astronomen des Altertums auf sich gezogen, sondern auch der Mondzyklus mit den verschiedenen Mondphasen sowie das Auftreten von Mondfinsternissen bei Vollmond und von Sonnenfinsternissen bei Neumond. Es gibt einen Zyklus, der die Zeit beschreibt, nachdem die Sonne und der Mond die gleiche Konstellation erreichen. Nach '''19 Jahren''' (beziehungsweise knapp '''6940 Tagen''') hat nicht nur die Sonne dieselbe ekliptikale Länge erreicht, sondern auch der der Mond (nach '''254 siderischen Monaten'''), und er hat daher auch dieselbe Mondphase (nach '''235 synodischen Monaten'''). Außerdem hat er dann auch noch annährend die gleiche ekliptikale Breite (nach '''255 drakonitischen Monaten'''), so dass er fast wieder an derselben Stelle des Fixsternhimmels steht.<ref name="rutherforth">Thomas Rutherforth: "A System Of Natural Philosophy: Being A Course of Lectures In Mechanics, Optics, Hydrostatics, and Astronomy; Which are Read in St Johns College Cambridge", volume 2, chapter XIV: "Of the devision<!--sic!--> of time", paragraph 388: "The cycle of Metos", 990 ff.</ref>
Der Zyklus beruht also im Wesentlichen auf der zwar nur langfristig, bei entsprechender Ausdauer jedoch einfach zu beobachtenden Tatsache, dass 19 tropische Sonnenjahre, 235 synodische Monate, 254 siderische Monate und 255 drakonitische Monate fast die gleiche Länge haben. Der Unterschied zwischen den ersten beiden beträgt nur rund zwei Stunden:
* 19 Jahre = 6939,6 Tage, bestimmt durch die ekliptikale Länge der Sonne mit ihren Äquinoktien und Sonnenwenden.
* 235 synodische Monate = 6939,7 Tage, bestimmt durch die Mondphasen mit Neumonden und Vollmonden.
* 254 siderische Monate = 6939,7 Tage, bestimmt durch die ekliptikale Länge des Mondes.
* 255 drakonitische Monate = 6939,1 Tage, bestimmt durch die auf- und absteigenden Knoten der Mondbahn.
[[Datei:Legende.Meton-Zyklus.png|mini|rechts|Legende zum Diagramm "Meton-Zyklus".]]
<gallery caption="Der Meton-Zyklus" mode="packed">
Meton-Zyklus.Maerz.2019.png|Der Meton-Zyklus anhand der Frühlingsvollmonde (weiße Kreise) am 21. März 2019 (laufende Nummer 0) und am 21. März 2038 (laufende Nummer 235).
Meton-Zyklus.Januar.2023.Januar.2042.png|Der Meton-Zyklus vom Vollmond am 6. Januar 2023 bis zum Vollmond am 6. Januar 2042. Die farbigen Kreise symbolisieren die Äquinoktien und Sonnenwenden.
</gallery>
[[Datei:Meton-Zyklus.2023.13996.png|mini|links|hochkant=4|Der 19-jährige Meton-Zyklus vom Januar 2023 bis zum Januar 2042. Die Zeit schreitet in horizontaler Richtung jeweils um einen halben Tag pro Bildpunkt voran. Der Vollmond (weißer Kreis) erscheint sowohl am 6. Januar 2023 als auch am 6. Januar 2042 im Sternbild Zwillinge (Gemini) nördlich vom Stern Mebsuta (ε Geminorum). Unten sind die kleinen und großen Mondwenden für die geographische Breite 52,5 Bogengrad (zum Beispiel in Berlin) im Laufe der Zeit dargestellt.]]
<div style="clear:both"></div>
Dieser 19-jährige nach dem antiken griechischen Astronomen {{w|Meton}} (5. Jahrhundert vor Christus) benannte '''Meton-Zyklus''' sowie auch der unten erwähnte Saros-Zyklus waren im Altertum spätestens schon den Babyloniern bekannt und dienten als Grundlage für ihren Mondkalender. Meton ist davon ausgegangen, dass 19 Jahre exakt mit 6940 Tagen sowie mit 235 synodischen Monaten übereinstimmen. Dadurch, dass das Jahr nach dieser Annahme genau fünf Neunzehntel Tage länger ist als 365 Tage, sind neunzehn Jahre nach dieser Berechnung genau fünf Tage länger ist als neunzehn Mal 365 Tage, also 6935 Tage. Aus der Annahme einer festen ganzrationalen Kopplung der Umlaufzeiten der Erde um Ihre Achse (Tag) und um die Sonne (Jahr) sowie der Umlaufzeit des Mondes um die Erde (Monat) ergeben sich die folgenden Zusammenhänge:
* Abgerundet auf ganze Zahlen:
** '''Die Jahreslänge in ganzen Tagen:'''
**:<math>\frac {6940} {19} = 365 \text { Rest } 5</math>, das heißt, dass für 19 Jahre mit der Länge 365 Tage '''fünf Schalttage''' (Jahreslänge dann 366 Tage) erforderlich sind, damit der Frühlingspunkt mit dem tropischen Sonnenjahr synchron bleibt (Solarkalender).
** '''Die Monatslänge in ganzen Tagen:'''
**:<math>\frac {6940} {235} = 29 \text { Rest } 125</math>, das heißt, dass für 235 synodische Monate mit der Länge 29 Tage '''125 Schalttage''' (Monatslänge dann 30 Tage) erforderlich sind, damit ein tropisches Sonnenjahr immer zwölf Monate umfasst (Solarkalender).
** '''Die Jahreslänge in ganzen Monaten:'''
**:<math>\frac {\frac {6940} {19}} {\frac {6940} {235}} = \frac {235} {19} = 12 \text { Rest } 7</math>, das heißt, dass in 19 Jahren mit 235 synodischen Monaten sowie 6490 Tagen '''sieben synodische Schaltmonate''' (Jahreslänge dann 13 Monate) erforderlich sind, um das Kalenderjahr mit dem tropischen Sonnenjahr synchron zu halten (Lunisolarkalender).
* Exakt mit Brüchen (ganzrationale Zahlen):
** Die Jahreslänge <math>d_a</math> in Tagen (in einem Sonnenjahr):
**:<math>d_a = \frac {6940 \text { d}} {19 \text { a}} = 365 \frac {\text {d}} {\text {a}} + \frac {5} {19} \frac {\text {d}} {\text {a}} = 365 \frac {\text {d}} {\text {a}} + \frac {20} {76} \frac {\text {d}} {\text {a}} = 365 \frac {\text {d}} {\text {a}} + \frac {1} {4} \frac {\text {d}} {\text {a}} + \frac {1} {76} \frac {\text {d}} {\text {a}} = 365,25 \frac {\text {d}} {\text {a}} + \frac {1} {76} \frac {\text {d}} {\text {a}} \approx 365,263158 \frac {\text {d}} {\text {a}}</math>
** Die Länge eines synodischen Monats <math>d_m</math> in Tagen:
**:<math>d_m = \frac {6940 \text { d}} {235 \text { m}} = \frac {1388} {47} \frac {\text {d}} {\text {m}} = 29 \frac {\text {d}} {\text {m}} + \frac {125} {235} \frac {\text {h}} {\text {m}} = 29 \frac {\text {d}} {\text {m}} + \frac {25} {47} \frac {\text {h}} {\text {m}} \approx 29,531915 \frac {\text {d}} {\text {m}}</math>
** Länge von zwölf synodischen Monaten <math>d_{m_{12}}</math> in Tagen (in einem Mondjahr):
**:<math>d_{m_{12}} = 12 \frac {\text {m}} {\text {a}} \cdot d_m = 12 \frac {\text {m}} {\text {a}} \cdot \frac {1388} {47} \frac {\text {d}} {\text {m}} = \frac {16656} {47} \frac {\text {d}} {\text {a}} \approx 354,382979 \frac {\text {d}} {\text {a}} \approx d_a - 11 \frac {\text {d}} {\text {a}}</math>
** Die Jahreslänge <math>m_a</math> in synodischen Monaten (in einem Sonnenjahr):
**:<math>m_a = \frac {d_a} {d_m} = \frac {\frac {6940 \text { d}} {19 \text { a}}} {\frac {6940 \text { d}} {235 \text { m}}} = \frac {235} {19} \frac {\text {m}} {\text {a}} = 12 \frac {\text {m}} {\text {a}} + \frac {7} {19} \frac {\text {m}} {\text {a}} \approx 12,368421 \frac {\text {m}} {\text {a}}</math>
:'''Anmerkung''': Man nehme zur Kenntnis, dass das der Mittelwert der Dauern vom Sonnenjahr <math>d_a</math> und vom Mondjahr <math>d_{m_{12}}</math> fast genau 360 Tage pro Jahr beträgt, also so viele Tage wie für einen vollständigen Kreis in Bogengrad gerechnet wird:
::<math>\frac {d_a + d_{m_{12}}} {2} = \frac {\frac {6940 \text { d}} {19 \text { a}} + \frac {16656} {47} \frac {\text {d}} {\text {a}}} {2} = \frac {321322} {893} \frac {\text {d}} {\text {a}} \approx 359,823 \frac {\text {d}} {\text {a}} \approx 360 \frac {\text {d}} {\text {a}}</math>
::Mit den heutigen, jeweils rund eine halbe Stunde kürzeren Messwerten für die beiden Jahresdauern (tropisches Sonnenjahr mit 365,241 Tagen und Mondjahr mit zwölf Lunationen und 354,367 Tagen) zur Epoche J2000.0 ergibt sich ein nur geringfügig anderer Mittelwert, der ebenfalls nur um zirka eine Dreiviertelstunde von der Dauer von 360 Tagen abweicht:
::<math>\frac {d_a + d_{m_{12}}} {2} = \frac {365,241 \frac {\text {d}} {\text {a}} + 354,367 \frac {\text {d}} {\text {a}}} {2} \approx 359,804 \frac {\text {d}} {\text {a}} \approx 360 \frac {\text {d}} {\text {a}}</math>
Für diese Erkenntnisse ist entweder die Weitergabe von beobachteten astronomischen Ereignissen, wie der Bedeckung der Plejaden durch den Mond oder die Messung der ekliptikalen Koordinaten des Mondes, an die nächste Generation erforderlich oder ein Lebensalter, das die Beobachtung von mindestens zwei solcher Zyklen umfasst – je nach Zeitpunkt der Geburt also rund 25 bis über 40 Jahre.
Da der Meton-Zyklus mit den aufgerundeten ganzzahligen 6940 Tagen einen Vierteltag länger dauert als 19 Sonnenjahre, summiert sich diese Differenz nach vier Meton-Zyklen auf einen ganzen Tag. Der entsprechende nach {{w|Kallippos von Kyzikos}} (viertes vorchristliches Jahrhundert) benannte '''Kallippische Zyklus''' von 76 Jahren (<math>= 4 \cdot 19</math> Jahre) oder 27759 Tage (<math>= (4 \cdot 6940) - 1</math> Tage) wird auch als verbesserter Meton-Zyklus bezeichnet:
* 76 Jahre = 27758,4 Tage
* 940 synodische Monate = 27758,8 Tage
* 1016 siderische Monate = 27758,8 Tage
* 1020 drakonitische Monate = 27756,5 Tage
Nach ungefähr 48 Sonnenjahren betrug die Differenz zwischen Meton-Zyklus und Sonnenjahr einen Tag, aber erst nach ungefähr 128 Sonnenjahren erreicht die Differenz zwischen Kallippischen Zyklus und Sonnenjahr so groß. Da der Kallippische Zyklus mit genau 27759;Tagen einen Vierteltag länger dauert als 76 Sonnenjahre, summiert sich diese Differenz nach vier Kallippischen Zyklen auf einen ganzen Tag. Der entsprechende nach {{w|Hipparchos (Astronom)|Hipparchos von Nicäa}} (zweites vorchristliches Jahrhundert) benannte '''Hipparchos-Zyklus''' von 304 Jahren (<math>= 4 \cdot 76 = 16 \cdot 19</math> Jahre) = 111035 Tage (<math>= (4 \cdot 27759) - 1</math> Tage) ist also wiederum ein verbesserter Kallippischer Zyklus:
* 304 Jahre = 111033,6 Tage
* 3760 synodische Monate = 111035,0 Tage
* 4064 siderische Monate = 111035,2 Tage
* 4080 drakonitische Monate = 111025,9 Tage
Nach einer Kallippischen Periode beträgt die Differenz zwischen 111035 ganzen Tagen und der Dauer von 3760 synodischen Monaten nur eine dreiviertel Stunde.
Die folgende Tabelle gibt einen Überblick über die Dauern im Meton-Zyklus, im Kallippischen Zyklus und im Hipparchos-Zyklus:
{| class="wikitable"
!
!Tropische Perioden
!Synodische Perioden
!Siderische Perioden
!Ganzzahlige Tage
|-
|'''Dauer einer Periode in Tagen'''
|'''365,2422'''
|'''29,53059'''
|'''27,32166'''
|'''1'''
|-
|
|-
|'''Anzahl der Perioden im Meton-Zyklus'''
|'''19'''
|'''235'''
|'''254'''
|'''6940'''
|-
|'''Anzahl der Tage im Meton-Zyklus'''
|6939,6018
|6939,6887
|6939,7016
|6940
|-
|'''Differenz zu tropischen Perioden in Tagen'''
|0,0000
|0,0868
|0,0998
|0,3982
|-
|'''Differenz zu synodischen Perioden in Tagen'''
| -0,0868
|0,0000
|0,0130
|0,3113
|-
|'''Differenz zu siderischen Perioden in Tagen'''
| -0,0998
| -0,0130
|0,0000
|0,2984
|-
|'''Differenz zu tropischen Perioden in Stunden'''
|0,00
|2,08
|2,40
|9,56
|-
|'''Differenz zu synodischen Perioden in Stunden'''
| -2,08
|0,00
|0,31
|7,47
|-
|'''Differenz zu siderischen Perioden in Stunden'''
| -2,40
| -0,31
|0,00
|7,16
|-
|
|-
|'''Anzahl der Perioden im Kallippischen Zyklus'''
|'''76'''
|'''940'''
|'''1016'''
|'''27759'''
|-
|'''Anzahl der Tage im Kallippischen Zyklus'''
|27758,4072
|27758,7546
|27758,8066
|27759
|-
|'''Differenz zu tropischen Perioden in Tagen'''
|0,0000
|0,3474
|0,3994
|0,5928
|-
|'''Differenz zu synodischen Perioden in Tagen'''
| -0,3474
|0,0000
|0,0520
|0,2454
|-
|'''Differenz zu siderischen Perioden in Tagen'''
| -0,3994
| -0,0520
|0,0000
|0,1934
|-
|'''Differenz zu tropischen Perioden in Stunden'''
|0,00
|8,34
|9,58
|14,23
|-
|'''Differenz zu synodischen Perioden in Stunden'''
| -8,34
|0,00
|1,25
|5,89
|-
|'''Differenz zu siderischen Perioden in Stunden'''
| -9,58
| -1,25
|0,00
|4,64
|-
|
|-
|'''Anzahl der Perioden im Hipparchos-Zyklus'''
|'''304'''
|'''3760'''
|'''4064'''
|'''111035'''
|-
|'''Anzahl der Tage im Hipparchos-Zyklus'''
|111033,6288
|111035,0184
|111035,2262
|111035
|-
|'''Differenz zu tropischen Perioden in Tagen'''
|0,0000
|1,3896
|1,5974
|1,3712
|-
|'''Differenz zu synodischen Perioden in Tagen'''
| -1,3896
|0,0000
|0,2078
| -0,0184
|-
|'''Differenz zu siderischen Perioden in Tagen'''
| -1,5974
| -0,2078
|0,0000
| -0,2262
|-
|'''Differenz zu tropischen Perioden in Stunden'''
|0,00
|33,35
|38,34
|32,91
|-
|'''Differenz zu synodischen Perioden in Stunden'''
| -33,35
|0,00
|4,99
| -0,44
|-
|'''Differenz zu siderischen Perioden in Stunden'''
| -38,34
| -4,99
|0,00
| -5,43
|}
Vor gut 2000 Jahren betrug die Differenz zwischen Kallippischem Zyklus und Sonnenjahr nach ungefähr 227 Sonnenjahren einen Tag. Durch die inzwischen etwas verkürzte Dauer eines tropischen Jahres ist dies heute bereits nach etwa 221 Jahren der Fall.
====Die Goldene Zahl====
Die '''Goldene Zahl''' gibt an, das wievielte von diesen 19 Jahren ein bestimmtes Jahr ist, und sie spielt auch heute noch eine wichtige Rolle bei der Bestimmung des Datums von Pessach oder Ostern, zum Beispiel mit Hilfe der Formeln zur Berechnung von {{w|Carl Friedrich Gauß}} (* 1777; † 1855):
→ Siehe auch:
* [[Astronomie von der Frühgeschichte bis zur Neuzeit/ Kalenderführung/ Osterdatum|'''Osterdatum''']]
* {{w|Gaußsche Pessach-Formel}}
* {{w|Gaußsche Osterformel}}
Der Name Goldene Zahl rührt möglicherweise davon her, dass der diesem Zyklus zugrundeliegende Kalender ('''Parapegma''') des Meton auf den Steinmauern seiner Sonnenuhr (heliotropion) am Pnyx-Hügel in Athen in goldener Schrift zu sehen war.<ref>Michael Wright: [https://www3.astronomicalheritage.net/index.php/show-entity?identity=26&idsubentity=1 The Pnyx, Athens, Greece], Portal to the Heritage of Astronomy, August 2011</ref><ref name="rutherforth" />
Heute ist in den Monaten um die Wintersonnenwende alle 19 Jahre morgens am westlichen Horizont der untergehende Vollmond im Goldenen Tor der Ekliptik zu sehen, wie im Dezember 2018. Die untere Hälfte des Mondes wird dann während des Untergangs vom Horizont verdeckt und der sichtbare leuchtende Teil bildet somit einen Halbkreis, wie er im mittleren Segment der Himmelstafel angedeutet ist. In diesem Fall liegen Hyaden und Plejaden im Westen auf einer Linie parallel zum Horizont und der dazwischenliegende, beim Untergang noch halb zu sehende Vollmond würde der Abbildung auf der Steintafel von Tal-Qadi entsprechen. Vor 4500 Jahren ergab sich diese Himmelsansicht wegen der Verschiebung des Frühlingspunktes bereits um die Tag-und-Nacht-Gleiche im Herbst.
Die 19 Megalithe des Blaustein-Hufeisens von '''Stonehenge''' (2270 bis 1930 vor Christus) werden ebenfalls mit dem 19-jährigen Meton-Zyklus in Zusammenhang gesehen.
Im Übrigen werden auch die Goldhüte aus der Bronzezeit mit diesem Zyklus in Verbindung gebracht. Der '''Berliner Goldhut''' zeigt in der siebenten und in der neunten ringförmigen Zone von oben horizontale Reihen mit 57 (= 3 x 19) kreisförmigen Punzen, die für ein Viertel der 228 Monate einer Meton-Periode stehen könnten.<ref>Wilfried Menghin: „Der Berliner Goldhut und die goldenen Kalendarien der alteuropäischen Bronzezeit“, Acta Praehistorica et Archaeologica, Band 32, 2000, ISSN 0341-1184, Seiten 31 bis 108</ref>
An der östlichen Wand in der Kamme A des hethitischen Heiligtums '''Yazılıkaya''' (türkisch für „beschriebener Fels“) aus dem 13. vorchristlichen Jahrhundert existiert eine Reliefbilderreihe, die eine Prozession von '''neunzehn''' nach links schauenden Göttinnen im Ganzkörperprofil darstellt. Auch hier wird vermutet, dass diese Reihe als Zählwerk für den Meton-Zyklus eine Kalenderfunktion innehatte.<ref>Eberhard Zangger, Rita Gautschy: [http://63.33.38.154/JSA/article/view/12232 Celestial Aspects of Hittite Religion - An Investigation of the Rock Sanctuary Yazilikaya], Journal of Skyscape Archaeology, 5(1), 5–38, 2019</ref><ref>Edwin C. Krupp, Eberhard Zangger: [https://www.archaeologie-online.de/artikel/2021/die-symbolische-darstellung-des-kosmos-im-hethitischen-felsheiligtum-yazlkaya Die symbolische Darstellung des Kosmos im hethitischen Felsheiligtum Yazılıkaya] vom 16. Juni 2021, Archäologie Online, archaeomedia, Freiburg</ref>
Der Zyklus mit der Anzahl 19 taucht in weiteren archäoastronomischen Kontexten auf:
* Mond-Sonne-Darstellung aus dem mesoamerikanischen '''Codex Borbonicus''' der Azteken vom Anfang des 16. Jahrhunderts.
* [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Mondzyklen#Der_Kalenderstein_vom_Tempel_Mnajdra|Oberste Lochreihe auf dem östlichen Kalenderstein vom Tempel '''Mnajdra''' auf Malta]].
* [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Höhlenmalerei_in_der_Magura-Höhle#Kalender|Kalender unter den Höhlenmalereien in der '''Magura'''-Höhle mit einer Matrix aus 19 Punkten]].
<gallery caption="Die Zahl 19 in archäoastronomischen Kontexten" mode=packed widths=360 heights=360>
Magura_cave_calendar_1528.jpg|Detail mit Elementen eines Kalenders aus der '''Magura'''-Höhle in Bulgarien. Unten in der Mitte befindet sich ein Feld mit 19 Punkten.
Kalenderstein.Mnajdra.labelled.png|Skizze der Lochreihen auf dem Kalenderstein von '''Mnajdra''' auf Malta. Die oberste Lochreihe besteht aus 19 Löchern.
Hattusa,_capital_of_the_Hittite_Empire_51.jpg|Detail mit den linken drei der insgesamt neunzehn Göttinnen der Bilderreihe in der Kammer A des hethitischen Heiligtums '''Yazılıkaya'''.
Yazilikaya.19.Gottheiten.png|Schmatische Darstellung der 19 weiblichen Gottheiten im hethitischen Heiligtum '''Yazılıkaya''' in der Türkei. Im Original variieren die Darstellungen der einzelnen Figuren, die in unterschiedlichem Zustand erhalten sind. Über den Händen einzelner Figuren konnten verschiedene hieroglyphische Darstellungen festgestellt werden. Die erste Figur (ganz links) konnte hierdurch nur indirekt rekonstruiert werden.
Berliner.Goldhut.P1068203.jpg||'''Berliner Goldhut''' vom Ende der Bronzezeit (1000–800 vor Christus) im Neuen Museum.
MesoamerikanischerMond.CodexBorbonicus.png|Mond-Sonne-Darstellung aus dem mesoamerikanischen '''Codex Borbonicus''' der Azteken vom Anfang des 16. Jahrhunderts. Die beiden sich zu einem vollen Kreis ergänzenden Halbkreise symbolisieren den Mond und die Sonne. Im Halbkreis des Mondes befinden sich '''19 Kreise'''.
</gallery>
====MUL.APIN====
[[Datei:MUP.APIN.VAT09412.P1078712.jpg|thumb|1914 gefundene Tontafel mit Texten aus dem 687 vor Christus geschrieben babylonischen Kompendium MUL.APIN, Irak, Assur/Qal'at Scherqat, VAT 09412, Neues Museum Berlin]]
Die auf den MUL.APIN-Tafeln schriftlich überlieferten babylonischen [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Plejaden#Schaltregeln|'''Plejaden-Schaltregeln''']] beziehen sich zweifelsfrei auf einen Zeitpunkt, als der offene Sternhaufen der Plejaden im Sternbild Stier (Taurus) die gleiche ekliptikale Länge wie der Frühlingspunkt gehabt hat. Dies bedeutet, dass die Sonne zur Tag-und-Nacht-Gleiche direkt neben den Plejaden stand, und dies war um 2600 vor Christus der Fall. Aus beiden Schaltregeln ergibt sich, dass innerhalb von 19 Sonnenjahren sieben zusätzliche synodische Monate eingeschaltet werden müssen, damit Sonnen- und synodisches Mondjahr synchron bleiben. Die Differenz zwischen Mondlauf und Sonnenlauf beträgt nach diesen 19 Sonnenjahren nur gut zwei Stunden. Diese Abweichung beträgt nur 0,0125 Promille beziehungsweise 12,58 parts per million (ppm):
* Tropisches Sonnenjahr (von Frühlingspunkt zu Frühlingspunkt): <math>J_S = 365,2422 \text { d} = 8765,813 \text { h}</math>
* Synodischer Monat (von Neumond zu Neumond): <math>M_{syn} = 29,53059 \text { d} = 708,7342 \text { h}</math>
* Synodisches Mondjahr (zwölf synodische Monate): <math>J_{syn} = 12 \cdot M_{syn} = 354,3671 \text { d} = 8504,810 \text { h}</math>
* Jahresdifferenz zwischen tropischem Sonnenjahr und synodischem Mondjahr: <math>\Delta J = J_S - J_{syn} = 10,8751 \text { d} = 261,003 \text { h}</math>
* Meton-Zyklus: <math>19 \cdot \Delta J = 206,627 \text { d} = 4959,046 \text { h}</math> beziehungsweise <math>7 \cdot M_{syn} = 206,714 \text { d} = 4961,139 \text { h}</math>
* Genauigkeit des Meton-Zyklus: <math>\Delta M = 7 \cdot M_{syn} - 19 \cdot \Delta J = 0,087 \text { d} \approx 2,09 \text { h} = 235 \cdot M_{syn} - 19 \cdot J_S</math>
* Arithmetische Zusammensetzung und Primfaktorenzerlegung der Anzahl synodischer Mondzyklen: <math>235 = 7 + 19 \cdot 12 = 5 \cdot 47</math>
Mit anderen Worten war der Meton-Zyklus bereits über zwei Jahrtausende lang bekannt, bevor sich der griechische Astronom Meton damit beschäftigte.
Es ist äußerst bemerkenswert, dass eine entsprechende Berechnung auch für dreizehn siderische Monate durchgeführt werden kann, bei denen der Mond jeweils einmal vollständig die Ekliptik mit ihren zwölf Ekliptiksternbildern beziehungsweise den 27 oder 28 Mondstationen (auch Mondhäuser genannt, siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Konjunktionen#Mondhäuser|Exkurs '''Mondhäuser''']]) durchläuft:
* Tropisches Sonnenjahr (von Frühlingspunkt zu Frühlingspunkt): <math>J_S = 365,2422 \text { d} = 8765,813 \text { h}</math>
* Siderischer Monat (von Fixstern zu Fixstern): <math>M_{sid} = 27,32166 \text { d} = 655,720 \text { h}</math>
* Siderisches Mondjahr (13 siderische Monate): <math>J_{sid} = 13 \cdot M_{sid} = 355,1816 \text { d} = 8524,358 \text { h}</math>
* Jahresdifferenz zwischen tropischem Sonnenjahr und siderischem Mondjahr: <math>\Delta J = J_S - J_{sid} = 10,0606 \text { d} = 241,454 \text { h}</math>
* Meton-Zyklus: <math>19 \cdot \Delta J = 191,151 \text { d} = 4587,634 \text { h}</math> beziehungsweise <math>7 \cdot M_{sid} = 191,252 \text { d} = 4590,040 \text { h}</math>
* Genauigkeit des Meton-Zyklus: <math>\Delta M = 7 \cdot M_{sid} - 19 \cdot \Delta J = 0,100 \text { d} \approx 2,40 \text { h} = 254 \cdot M_{sid} - 19 \cdot J_S</math>
* Arithmetische Zusammensetzung und Primfaktorenzerlegung der Anzahl siderischer Mondzyklen: <math>254 = 7 + 19 \cdot 13 = 2 \cdot 127</math>
Auch hier beträgt die Differenz zwischen Mondlauf und Sonnenlauf nur gut zwei Stunden. Und innerhalb der 19 Sonnenjahre eines Meton-Zyklus müssen auch hier sieben zusätzliche (vierzehnte) siderische Monate eingeschaltet werden, damit Sonnen- und siderisches Mondjahr synchron bleiben.
Zur Differenz von knapp elf Tagen zwischen Sonnen- und Mondjahr siehe auch '''[[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Plejaden#Das_babylonische_Neujahrsfest_Akiti|das babylonische Neujahrsfest Akiti]]'''. Dieses elftägige Fest wurde am Ende des Mondjahres gefeiert.
===Der drakonitische Zyklus===
Ferner existiert ein zirka '''18,61-jähriger Mondzyklus''', der darauf beruht, dass bedingt durch die Präzession der Mondbahn der aufsteigende und der absteigende Mondknoten nach dieser Zeit die Ekliptik entgegen der rechtläufigen (prograden) Umlaufrichtung des Mondes genau einmal vollständig rückläufig (retrograd) durchlaufen haben. Dieser Zyklus besteht aus 249,83 drakonitischen Monaten, die insgesamt 6798,38 Tagen beziehungsweise 18,61 tropischen Sonnenjahren entsprechen. Die ekliptikalen Längen der Mondknoten vermindern sich hierbei um einen Winkel von 19,34 Bogengrad pro Jahr.
Dieser drakonitische Zyklus ist zum Beispiel anhand der Abweichungen der ekliptikalen Breiten des Mondes und somit der Azimute bei den monatlichen Mondaufgängen und Monduntergängen am Horizont zu beobachten, die sich nach 18,61 Jahren wiederholen und dabei um die Punkte der Wintersonnenwende im Südosten und Südwesten sowie die Punkte der Sommersonnenwende im Nordosten und Nordwesten entlang dem Horizont pendeln. Die Zeitpunkte an dem die entsprechenden Auf- und Untergangspunkte zwischen dem nördlichen und dem südlichen Horizont um die Punkte der Tag-und-Nacht-Gleichen im Osten und Westen, die definitionsgemäß bei der ekliptikalen Breite null genau in der Ekliptik liegen, am engsten beziehungsweise am weitesten auseinanderliegen, heißen '''große und kleine Mondwenden'''. Diese Mondwenden wiederholen sich also alle 18,61 Jahre. Hierbei treten auch größte und kleinste Mondwenden auf. Um die Wintersonnenwenden der Jahre 2023, 2042, 2061 und so weiter treten beispielsweise '''größte Mondwenden''' auf, bei denen die Auf- und Untergangspunkte maximal in Richtung Norden von den genau im Osten und im Westen liegenden Äquinoktialpunkten entfernt sind. Und um die Sommersonnenwenden der Jahre 2024, 2043, 2062 und so weiter treten '''kleinste Mondwenden''' auf, bei denen die Auf- und Untergangspunkte maximal in Richtung Süden von den genau im Osten und im Westen liegenden Äquinoktialpunkten entfernt sind.
[[Datei:Ekliptik.helle.Objekte.png|mini|hochkant=2|rechts|Die sieben hellsten feststehenden Himmelsobjekte in der Nähe der Ekliptik liegen zwischen den Sternbildern Stier (Taurus, rechts) und Skorpion (Scorpio, links). Der Bogen der Ekliptik wird von den Wandelgestirnen entgegen dem Uhrzeigersinn vom Frühlingspunkt rechts zum Herbstpunkt links durchlaufen. In der Nähe unteren Bogenhälfte befinden sich keine hellen Fixsterne in der Nähe der Ekliptik, der helle Stern Fomalhaut (α Piscis Austrini) im Sternbild Südlicher Fisch (Piscis Austrinus) dient lediglich zur Orientierung. Außerhalb des Bogens liegende Punkte befinden sich nördlich der Ekliptik und innen liegende südlich.]]
Aufgrund dieser Zusammenhänge werden alle möglichen Positionen des Mondes in Bezug auf die Ekliptik bei den ekliptikalen Längen von -180 bis +180 Bogengrad und den ekliptikalen Breiten von ungefähr -6 bis +6 Bogengrad innerhalb dieser 18,61-jährigen Periode erreicht. Somit erfolgen auch alle möglichen Sternbedeckungen (Okkultationen) oder nahe Konjunktionen innerhalb dieser Periodendauer und wiederholen sich danach im drakonitischen Zyklus. Die Bedeckungen hellsten ekliptiknahen Himmelsobjekte sind hierbei besonders spektakulär und gut zu beobachten. Dies gilt insbesondere für:
* die '''Plejaden''' (Messier 45, 1,5<sup>m</sup>) im Sternbild Stier (Taurus)
* die '''Hyaden''' (0,5<sup>m</sup>) im Sternbild Stier (Taurus)
* den Roten Riesen '''Aldebaran''' (α Tauri, 1,0<sup>m</sup>) im Sternbild Stier (Taurus)
* den Roten Überriesen '''Antares''' (α Scorpii, 1,0<sup>m</sup>) im Sternbild Skorpion (Scorpio),
* den Stern '''Spica''' (α Virginis, 1,0<sup>m</sup>) im Sternbild Jungfrau (Virgo)
* den Stern '''Regulus''' (α Leonis, 1,5<sup>m</sup>) im Sternbild Löwe (Leo)
Wenn der Mond bei der Bedeckung der '''Plejaden''' seine maximale nördliche ekliptikale Breite bereits zuvor erreicht hatte und sich also bereits wieder in Richtung seines absteigenden Knotens bewegt, befindet sich in der Nähe des absteigenden Knotens der Königsstern '''Regulus''' im Sternbild Löwe (Leo), so dass es ungefähr eine Woche später ebenfalls zu dessen Bedeckung durch den Mond kommen kann. Da der Stern '''Antares''' α Scorpii) im Sternbild Skorpion (Scorpio) in etwa die gleiche südliche ekliptikale Breite hat, wie die Plejaden eine nördliche ekliptikale Breite haben, und beide auf gegenüberliegenden Punkten der Ekliptiklinie liegen, kommt es im Rhythmus des drakonitischen Zyklus innerhalb eines halben Monats gehäuft zu der Bedeckung beider Himmelsobjekte durch die Mondscheibe. Beide Ereignisse wären wegen der gegenüberliegenden Lage auf der Ekliptik heute allerdings nur während der langen Nächte im Winterhalbjahr zu sehen, wenn Antares praktisch nicht zu beobachten ist, weil er von der Sonne überstrahlt wird. Als der Frühlingspunkt im Altertum jedoch im Sternbild Stier stand, war die aufeinanderfolgende Bedeckung von Plejaden und Antares jedoch durchaus ein zu beobachtendes Doppelereignis.
Das nächste Mal werden die beiden eng benachbarten Elternsterne der Plejaden (der Titan Atlas und die Okeanide Pleione) von Mitteleuropa aus gesehen in den Morgenstunden des 8. Augusts 2024 von der Scheibe des abnehmenden Halbmonds bedeckt. Am 1. April 2025 werden gegen Mitternacht dann sogar mehrere helle Sterne des Sternhaufens durch die nur vier Tage alte Mondsichel bedeckt.
Auch im alten chinesischen, mündlich überlieferten Volksmärchen „Morgenhimmel“ wird der Zyklus vom '''Stern des großen Jahres''' erwähnt, der sich erst nach 18 Jahren, also im 19. Jahr wiederholt:<ref>[[s:Morgenhimmel|Morgenhimmel]], Wikisource</ref>
<blockquote>
Als Morgenhimmel gestorben war, berief der Kaiser den Sterndeuter und fragte: „Kanntest du Morgenhimmel?“<br/>
Der sagte: „Nein.“<br/>
Der Kaiser fragte: „Was verstehst du denn?“<br/>
Der Sterndeuter sagte: „Ich kann nach den Sternen sehen.“<br/>
„Sind alle Sterne an ihrem Platz?“ fragte der Kaiser.<br/>
„Ja. Nur den Stern des großen Jahres habe ich achtzehn Jahre nicht gesehen. Jetzt aber ist er wieder sichtbar.“<br/>
Da blickte der Kaiser zum Himmel auf und seufzte: „Achtzehn Jahre lang war Morgenhimmel mir zur Seite, und ich wusste nicht, dass er der Stern des großen Jahres war.“
</blockquote>
Mit "Stern des großen Jahres" könnte ein Ereignis gemeint sein, bei dem der Mond alle 18,61 Jahre einen bestimmten hellen und ekliptiknahen Stern bedeckt, wie zum Beispiel einen der drei Königssterne Aldebaran (α Tauri) im Sternbild Stier (Taurus), Regulus (α Leonis) im Sternbild Löwe (Leo), Antares (α Scorpii) im Sternbild Skorpion (Scorpio) oder auch Spica (α Virginis) im Sternbild Jungfrau (Virgo).
Auch für den drakonitischen Zyklus kann für dreizehn drakonitische Monate eine Berechnung in Bezug auf den 19-jährigen Meton-Zyklus durchgeführt werden:
* Tropisches Sonnenjahr (von Frühlingspunkt zu Frühlingspunkt): <math>J_S = 365,2422 \text { d} = 8765,813 \text { h}</math>
* Drakonitischer Monat (von Mondknoten zu Mondknoten): <math>M_{dra} = 27,21222 \text { d} = 653,093 \text { h}</math>
* Drakonitisches Mondjahr (13 drakonitische Monate): <math>J_{dra} = 13 \cdot M_{dra} = 353,7589 \text { d} = 8490,213 \text { h}</math>
* Jahresdifferenz zwischen tropischem Sonnenjahr und drakonitischem Mondjahr: <math>\Delta J = J_S - J_{dra} = 11,4833 \text { d} = 275,600 \text { h}</math>
* Meton-Zyklus: <math>19 \cdot \Delta J = 218,1835 \text { d} = 5236,403 \text { h}</math> beziehungsweise <math>8 \cdot M_{sid} = 217,6978 \text { d} = 5224,746 \text { h}</math>
* Genauigkeit des Meton-Zyklus: <math>\Delta M = 8 \cdot M_{dra} - 19 \cdot \Delta J = -0,486 \text { d} \approx -11,66 \text { h} = -(255 \cdot M_{dra} - 19 \cdot J_S)</math>
* Arithmetische Zusammensetzung und Primfaktorenzerlegung der Anzahl drakonitischer Mondzyklen: <math>255 = 8 + 19 \cdot 13 = 3 \cdot 5 \cdot 17</math>
Hier beträgt die Differenz zwischen Mondlauf und Sonnenlauf knapp einen halben Tag. Innerhalb der 19 Sonnenjahre eines Meton-Zyklus müssen hier acht zusätzliche (vierzehnte) drakonitische Monate eingeschaltet werden, damit Sonnen- und drakonitisches Mondjahr synchron bleiben.
===Der Saros-Zyklus===
[[Datei:Saros-Zyklus.2023.Januar.png|rechts|mini|hochkant=4|Der '''Saros-Zyklus''' umfasst sowohl eine Periode von 242 drakonitischen Monaten als auch eine Periode von 223 synodischen Monaten. Das sind gut 6585 Tage oder 18,03 tropische Sonnenjahre, also 18 Jahre und knapp 11 Tage). In der Graphik ist die Zeit vom aufsteigenden Knoten (rot) am 1. Januar 2023 bis zum aufsteigenden Knoten (rot) am 11. Januar 2041 dargestellt. Man beachte die gleichzeitige zyklische Wiederholung der jeweiligen Mondphasen sowie daraus folgend der '''Finsternisse''' im April und Mai sowie im Oktober und November mit einer Verschiebung von elf Kalendertagen im Sonnenjahr.<br/>Die '''Mondwenden''' (die tagesaktuellen Auf- und Untergangsazimute des Mondes sind im unteren Bereich für die geographische Breite 52,5 Bogengrad (zum Beispiel in Berlin) dargestellt) haben ihr Minimum und Maximum demzufolge zu den entsprechenden Zeitpunkten, die Ausprägung dieser Extrema unterscheidet sich jedoch ein wenig, weil der Mond dort geringfügig abweichende minimale und maximale ekliptikale Breiten hat. Die kleinen Mondwenden beim Sommervollmond im Juli und die großen Mondwenden beim Wintervollmond (Jahreswechsel) sind in der Graphik ebenfalls nachvollziehbar.<br/>[[Datei:Legende.Meton-Zyklus.png|200px|zentriert|Legende]]]]
Über die weiter oben beschriebenen Koinzidenzen hinaus kann beobachtet werden, dass der Mond nach '''18,03 Jahren''' (also nach 242 drakonitischen Monaten beziehungsweise 6585,3 Tagen) denselben auf- oder absteigenden Knoten erreicht, wobei Sonne und Mond dann die gleiche Elongation haben (nach 223 synodischen Monaten beziehungsweise 6585,2 Tagen). Sie befinden sich dann allerdings nur fast bei den gleichen ekliptikalen Längen beziehungsweise an den gleichen Stellen des Fixsternhimmels, da diese Dauer nur mit ungefähr einem halben Tag Differenz mit 241 siderischen Perioden übereinstimmt (6584,6 Tage). Innerhalb dieses halben Tages hat sich die Sonne um zirka ein halbes Bogengrad und der Mond sogar um ungefähr sechseinhalb Bogengrad weiterbewegt.
Dieser Zyklus wird '''Saros-Zyklus''' genannt. Innerhalb dieser Zeitspanne ergibt sich eine Reihe von Sonnen- und Mondfinsternissen, die sich in ihrer Abfolge immer wieder ähneln. Der Saros-Zyklus war bei den Babyloniern bereits im Altertum bekannt, was in Keilschrift geschrieben auf Tontafeln aus der Mitte des achten vorchristlichen Jahrhunderts überliefert ist.
Heute werden die sich wiederholenden Sonnen- und Mondfinsternisse, die sich auf einen bestimmten auf- oder absteigenden Knoten der Mondbahn beziehen und jeweils eine Lebensdauer von mehreren Jahrhunderten haben, zum Zwecke der Unterscheidung mit Nummern versehen. Zu einem bestimmten Zeitpunkt der Weltgeschichte sind immer mehrere Dutzend Saros-Zyklen gleichzeitig aktiv.
Die anomalistische Periode steht für die Dauer, bis der Mond auf seiner elliptischen Bahn um die Erde erneut eines seiner beiden Apogäen erreicht, also das Perigäum (erdnächster Punkt) oder das Apogäum (erdfernster Punkt). Nach einer ganzzahligen vielfachen Dauer der anomalistischen Periode hat der Mond also stets fast die gleiche Entfernung zur Erde und somit auch die gleiche scheinbare Größe, so dass die Dauer und Art (totale oder ringförmige Sonnenfinsternis) einer Finsternis sich wiederholt.
Wegen der gleichen ekliptikalen Breiten des Mondes nach einer ganzzahligen vielfachen Dauer der drakonitischen Periode erscheint der Mondschatten auf der Erdoberfläche nach der Dauer einer Saros-Periode ungefähr beim gleichen Breitengrad. Durch die Differenz von rund einem drittel Tag ist der Längengrad allerdings nach einer Saros-Periode um knapp 120 Bogengrad nach Westen verschoben. Nach drei Saros Perioden, oder nach einem dadurch definierten Exeligmos-Zyklus, haben sich die drei Dritteltage fast auf einen Tag summiert, so dass nach der Mondschatten dann nicht nur wieder beim ähnlichen Breitengrad, sondern sogar beim ähnlichen Längengrad und zu einer ähnlichen Tageszeit auftaucht. Anders ausgedrückt: wenn an einem bestimmten Ort auf der Erdoberfläche eine Finsternis auftritt, so erfolgt in der Regel nach 19756 Tagen (54 Jahren und 33 Tagen) in dieser geographischen Region mit nur rund einer Stunde Verfrühung erneut eine solche Finsternis.
{| class="wikitable"
|+ Exeligmos-Zyklus
! Anzahl der Saros-Zyklen !! Dauer in tropischen Jahren !! Dauer in ganzen Tagen
|-
| 1 || 18,03 || 6585
|-
| 3 || 54,09 || 19756
|}
Die folgende Tabelle gibt einen Überblick über die Dauern verschiedener Mondzyklen in einem '''Saros-Zyklus''' (18,03 tropische Jahre) sowie in einem dreimal so langen Zyklus '''Exeligmos-Zyklus''' (54,09 tropische Jahre) .
{| class="wikitable"
!
!Synodische Perioden
!Drakonitische Perioden
!Tage
!Anomalistische Perioden
|-
|'''Dauer einer Periode in Tagen'''
|'''29,53059'''
|'''27,21222'''
|'''1'''
|'''27,53455'''
|-
|
|-
|'''Anzahl der Perioden im Saros-Zyklus'''
|'''223'''
|'''242'''
|'''6585'''
|'''239'''
|-
|'''Anzahl der Tage im Saros-Zyklus'''
|6585,3216
|6585,3572
|6585,0000
|6580,7575
|-
|'''Differenz zu synodischen Perioden in Tagen'''
|0,0000
|0,0357
| -0,3216
| -4,5641
|-
|'''Differenz zu drakonitischen Perioden in Tagen'''
| -0,0357
|0,0000
| -0,3572
| -4,5998
|-
|'''Differenz zu ganzen Tagen'''
|0,3216
|0,3572
|0,0000
| -4,2425
|-
|'''Differenz zu synodischen Perioden in Stunden'''
|0,00
|0,86
| -7,72
| -109,54
|-
|'''Differenz zu drakonitischen Perioden in Stunden'''
| -0,86
|0,00
| -8,57
| -110,39
|-
|'''Differenz zu ganzen Tagen in Stunden'''
|7,72
|8,57
|0,00
| -101,82
|-
|
|-
|'''Anzahl der Perioden im Exeligmos-Zyklus'''
|'''669'''
|'''726'''
|'''19756'''
|'''717'''
|-
|'''Anzahl der Tage im Exeligmos-Zyklus'''
|19755,9647
|19756,0717
|19756,0000
|19742,2724
|-
|'''Differenz zu synodischen Perioden in Tagen'''
|0,0000
|0,1070
|0,0353
| -13,6924
|-
|'''Differenz zu drakonitischen Perioden in Tagen'''
| -0,1070
|0,0000
| -0,0717
| -13,7994
|-
|'''Differenz zu ganzen Tagen in Tagen'''
| -0,0353
|0,0717
|0,0000
| -13,7277
|-
|'''Differenz zu synodischen Perioden in Stunden'''
|0,00
|2,57
|0,85
| -328,62
|-
|'''Differenz zu drakonitischen Perioden in Stunden'''
| -2,57
|0,00
| -1,72
| -331,18
|-
|'''Differenz zu ganzen Tagen in Stunden'''
| -0,85
|1,72
|0,00
| -329,46
|}
===Mondwenden===
Da sich die '''Sonne''' definitionsgemäß immer in der Ekliptikebene befindet, wiederholen sich die Verhältnisse in Bezug auf ihre Auf- und Untergangspunkte sowie auf ihre südlichen und die nördlichen Kulminationshöhen in jedem tropischen Sonnenjahr. Zur '''Sommersonnenwende''' steht die Sonne am höchsten und am längsten über den Horizont, die Auf- und Untergangsazimute erreichen ihre nördlichsten Positionen und es ergibt sich der Kalendertag mit dem längsten Tagesslicht und der kürzesten Dunkelheit. Zur '''Wintersonnenwende''' steht die Sonne am niedrigsten und am kürzesten über den Horizont, die Auf- und Untergangsazimute erreichen ihre südlichsten Positionen und es ergibt sich der Kalendertag mit dem kürzesten Tagesslicht und der längsten Nacht. Bei den beiden '''Äquinoktien''' zum '''Frühlings- und Herbstbeginn''' herrscht die '''Tag-und-Nacht-Gleiche''', zwölf Stunden Tag und zwölf Stunden Nacht. Die Sonne geht überall auf der Erde exakt im Osten auf beziehungsweise im Westen unter.
Mit dem Neumond verhält es sich ähnlich wie mit der Sonne, da er sich stets in Sonnennähe befindet. Die Mondbahn ist gegenüber der Ekliptik jedoch geneigt (Inklination der Mondbahn), so dass der Mond bis zu gut 5 Bogengrad höher oder niedriger stehen kann als die Sonnenscheibe. Bei einer '''großen Mondwende''' steht die Ekliptik immer besonders hoch über dem Horizont und der Mond hat gleichzeitig seine maximale nördliche ekliptikale Breite erreicht. Wenn der Vollmond bei einer großen Mondwende auf dem südlichen Meridian kulminiert, findet dieses Ereignis als '''größte Mondwende''' um Mitternacht zur Wintersonnenwende statt (im Dezember 2023, 2042 und 2061).
[[Datei:Grosse.Mondwende.png|mini|zentriert|hochkant=2|Die größte Mondwende bei der am höchsten stehenden Ekliptik auf der Nordhalbkugel sowie die Sommersonnenwende im Horizontsystem. Die blaue Mondbahn wird während der Sommersonnenwende nur bei Neumond gleichzeitig mit der orangefarbenen Sonnenbahn durchlaufen. Während der Wintersonnenwende findet diese große Mondwende statt, wenn der Mond in Opposition zur Sonne steht und somit Vollmond herrscht.]]
Bei einer '''kleinen Mondwende''' steht die Ekliptik immer besonders niedrig über dem Horizont und der Mond hat gleichzeitig seine minimale südliche ekliptikale Breite erreicht. Wenn der Vollmond bei einer kleinen Mondwende auf dem südlichen Meridian kulminiert, findet dieses Ereignis als '''kleinste Mondwende''' um Mitternacht zur Sommersonnenwende statt (im Juni 2024, 2043 und 2062).
[[Datei:Kleine.Mondwende.png|mini|zentriert|hochkant=2|Die kleinste Mondwende bei der am niedrigsten stehenden Ekliptik auf der Nordhalbkugel sowie die Wintersonnenwende im Horizontsystem. Die blaue Mondbahn wird während der Wintersonnenwende nur bei Neumond gleichzeitig mit der orangefarbenen Sonnenbahn durchlaufen. Während der Sommersonnenwende findet diese kleine Mondwende statt, wenn der Mond in Opposition zur Sonne steht und somit Vollmond herrscht.]]
[[Datei:ZunehmenderMond.Herbst-Aequinoktium.P1185517.jpg|mini|rechts|Zunehmender Mond zum Herbst-Äquinoktium bei kleiner Mondwende mit flacher Ekliptik in Berlin mit fast senkrecht stehender Mondsichel (38% beleuchtet) im Sternbild Skorpion kurz vor Sonnenuntergang. Visuelle Helligkeit -9 mag, südliche ekliptikale Breite = -5°, Höhe über dem südlichen Horizont = 9°, Mondalter 6,2 Tage.]]
In der folgenden Übersicht sind die '''Mondaufgangspunkte''' am östlichen Horizont, die '''Kulminationspunkte''' auf dem südlichen Meridian und die '''Monduntergangspunkte''' am westlichen Horizont für die vier verschiedenen Jahreszeiten angegeben:
{| class="wikitable"
|+ Die Lage des Bogens der Ekliptik über dem nächtlichen Horizont zu verschiedenen Jahreszeiten
!title="Jahreszeit"| Jahreszeit
!title="Am Abend"| Am Abend</br>(Aufgang des Vollmonds</br>in östlicher Richtung)
!title="Um Mitternacht"| Um Mitternacht</br>(Kulmination des Vollmonds</br>auf dem südlichen Meridian)
!title="Am Morgen"| Am Morgen</br>(Untergang des Vollmonds</br>in westlicher Richtung)
|-
| '''Frühlingsanfang'''</br>'''Tag-und-Nacht-Gleiche''' || [[Datei:Ekliptik.C.png|360px]] || [[Datei:Ekliptik.D.png|360px]] || [[Datei:Ekliptik.A.png|360px]]
|-
| '''Sommeranfang'''</br>'''Sommersonnenwende''' || [[Datei:Ekliptik.D.png|360px]] || [[Datei:Ekliptik.A.png|360px]] || [[Datei:Ekliptik.B.png|360px]]
|-
| '''Herbstanfang'''</br>'''Tag-und-Nacht-Gleiche''' || [[Datei:Ekliptik.A.png|360px]] || [[Datei:Ekliptik.B.png|360px]] || [[Datei:Ekliptik.C.png|360px]]
|-
| '''Winteranfang'''</br>'''Wintersonnenwende''' || [[Datei:Ekliptik.B.png|360px]] || [[Datei:Ekliptik.C.png|360px]] || [[Datei:Ekliptik.D.png|360px]]
|}
[[Datei:Orbit de Mondknoten.svg|mini|rechts|hochkant=2|Bahnellipse des Mondes mit den beiden Mondknoten.]]
Im Laufe eines '''drakonitischen Monats''' geht der Mond sowohl durch den aufsteigenden als auch durch den absteigenden Knoten (Drachenpunkte) seiner Mondbahn. Nach dem Durchqueren des aufsteigenden Knotens wird die ekliptikale Breite des Mondes positiv (nördliche ekliptikale Breite), er steigt in Bezug zur Ekliptiklinie nach Norden und bleibt entsprechend immer länger über dem Horizont. Nach dem Durchqueren des absteigenden Knotens wird die ekliptikale Breite des Mondes negativ (südliche ekliptikale Breite), er fällt in Bezug zur Ekliptiklinie nach Süden und bleibt entsprechend immer kürzer über dem Horizont. In der deutschsprachigen Schweiz gibt es für diese im Laufe eines drakonitischen Monats täglich mehr oder weniger deutlich wahrnehmbaren Änderungen der ekliptikalen Breite sogar eigene Adjektive. Das Ansteigen der ekliptikalen Breite des Mondes nach Norden wird '''obsigend''', und das Abfallen der ekliptikalen Breite des Mondes nach Süden wird '''nidsigend''' genannt.
[[Datei:Mond-.und.Sonnenwenden.png|mini|rechts|hochkant=2|Verschiedene Mond- und Sonnenwenden auf dem südlichen Meridian im Laufe eines Jahres in Bezug zum Horizontsystem.]]
Im Laufe der Zeit pendelt der Mond immer wieder zwischen den kleinen und großen Mondwenden. Dieser Ablauf wiederholt sich nach einem vollständigen '''drakonitischen Zyklus''', also nach 18,61 Jahren, innerhalb dessen die kleinste und die größte Mondwende im zeitlichen Abstand von rund 9,3 Jahren jeweils einmal erreicht wird.
Die Deklinationen der Sonne <math>\delta_S</math> pendeln also je nach Jahreszeit maximal um den Betrag der Schiefe der Ekliptik <math>\varepsilon</math> um den Nullpunkt <math>\delta_S = 0</math>, der nur zu den Tag-und-Nacht-Gleichen erreicht wird:
:<math>-\varepsilon \le \delta_S \le +\varepsilon</math>
Die Deklinationen des Mondes <math>\delta_M</math> pendeln zusätzlich um den Betrag der Inklination der Mondbahn <math>i</math> (heute '''5,145 Bogengrad''') um die Werte der Deklination der Sonne <math>\delta_S</math>:
:<math>-\varepsilon - i \le \delta_M \le +\varepsilon + i</math>
Die Deklination und der maximale Höhenwinkel bei der Kulmination des Mondes auf dem südlichen Meridian variieren also sowohl innerhalb eines Monats durch die Inklination der Mondbahn als auch jährlich durch die Neigung der Erdachse (also die Schiefe der Ekliptik).
Die für die Azimute der Mondaufgänge <math>a_{A,M}</math> anzuwendende Formel hängt nicht nur von der augenblicklichen Deklination des Mondes <math>\delta_M</math>, sondern auch vom geographischen Breitengrad des Beobachters <math>\varphi</math> ab:
:<math>\cos a_{A,M} = \frac {\sin \delta_M} {\cos \varphi}</math>
Aus Symmetriegründen ergibt sich für die Azimute der Monduntergänge <math>a_{U,M}</math>:
:<math>a_{U,M} = 360^\circ - a_{A,M} = 360^\circ - \arccos \frac {\sin \delta_M} {\cos \varphi}</math>
{| class="wikitable"
|+ Deklinationen des Mondes
! Ereignis !! Deklination des Mondes <math>\delta_M</math> !! Maximaler Höhenwinkel des Mondes<br/><math>h_{max,M}</math> || Azimut des Mondaufgangs<br/><math>a_{A,M}</math> || Azimut des Monduntergangs<br/><math>a_{U,M}</math>
|-
| Kleinste Mondwende<br/><math>\delta_S = -\varepsilon</math> || <math>-(\varepsilon + i)</math> || <math>(90^\circ - \varphi) - (\varepsilon + i)</math> || <math>\arccos \frac {\sin (-\varepsilon - i)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (-\varepsilon - i)} {\cos \varphi}</math>
|-
| Kleine Mondwende || <math>\delta_S - i</math> || <math>(90^\circ - \varphi) + (\delta_S - i)</math> || <math>\arccos \frac {\sin (\delta_S - i)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (\delta_S - i)} {\cos \varphi}</math>
|-
| Mond im Knoten || <math>\delta_S</math> || <math>(90^\circ - \varphi) + \delta_S</math> || <math>\arccos \frac {\sin (\delta_S)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (\delta_S)} {\cos \varphi}</math>
|-
| Große Mondwende || <math>\delta_S + i</math> || <math>(90^\circ - \varphi) + (\delta_S + i)</math> || <math>\arccos \frac {\sin (\delta_S + i)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (\delta_S + i)} {\cos \varphi}</math>
|-
| Größte Mondwende<br/><math>\delta_S = +\varepsilon</math> || <math>+(\varepsilon + i)</math> || <math>(90^\circ - \varphi) + (\varepsilon + i)</math> || <math>\arccos \frac {\sin (\varepsilon + i)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (\varepsilon + i)} {\cos \varphi}</math>
|}
Die Höhenwinkel des Mondes und der Sonne konnten zum Beispiel schon in der Bronzezeit mit Hilfe von '''Sonnenuhren''', '''Stabdolchen''' oder '''Kreisgrabenanlagen''' beobachtet und bestimmt werden. Im Laufe eines Jahres konnten damit die jahreszeitlichen Veränderungen der scheinbaren Sonnenbahn verfolgt werden. Durch eine langjährige Beobachtung war es dann auch möglich, die kleinsten und größten Mondwenden zu ermitteln.
Siehe hierzu auch:
* → '''[[Quadriviale_Kuriositäten/_Stabdolche|Stabdolche]]'''
* → '''[[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Kalenderführung#Frühgeschichtliche_Observatorien|Frühgeschichtliche Observatorien]]'''
===Vergleich der Perioden===
Die folgenden Periodendauern spielen bei Sonnen- und Mondzyklen eine besondere Rolle:
* '''Meton-Zyklus:'''
** 19 '''tropische Sonnenjahre''' = 6939,6 Tage, bestimmt durch die ekliptikale Länge der Sonne in Bezug zum Frühlingspunkt mit ihren Äquinoktien und Sonnenwenden.
** 235 '''synodische Monate''' = 6939,7 Tage, bestimmt durch die Mondphasen mit Neumonden und Vollmonden.
** 254 '''siderische Monate''' = 6939,7 Tage, bestimmt durch die ekliptikale Länge des Mondes in Bezug zum Frühlingspunkt und den Fixsternhimmel.
** 255 '''drakonitische Monate''' = 6939,1 Tage, bestimmt durch die ekliptikale Breite des Mondes beziehungsweise die auf- und absteigenden Knoten der Mondbahn.
* '''Drakonitischer Zyklus:'''
** 249,83 '''drakonitische Monate''' = 6798,38 Tage = 18,613 tropische Sonnenjahre, bestimmt durch die ekliptikalen Längen der auf- und absteigenden Knoten der Mondbahn: Zyklus der Mondwenden und Sternbedeckungen (Nutationsperiode)
* '''Saros-Zyklus:'''
** 242 '''drakonitische Monate''' = 6585,3 Tage = 18,03 tropische Sonnenjahre (respektive 18 Jahre und 11 Tage): Zyklen der Sonnen- und Mondfinsternisse
** 223 '''synodische Monate''' = 6585,4 Tage, bestimmt durch die Mondphasen mit Neumonden und Vollmonden.
[[Datei:Meton-Zyklus.2023.4x5.png|mini|links|hochkant=4|Der 19-jährige Meton-Zyklus vom Januar 2023 bis zum Januar 2042. Die Zeit schreitet in horizontaler Richtung jeweils um einen halben Tag pro Bildpunkt voran.<br/>Im unteren Teil des Diagramms sind die kleinen und großen Mondwenden für die geographische Breite 52,5 Bogengrad (zum Beispiel in Berlin) dargestellt.]]
<div style="clear:both"></div>
[[Datei:Herbstaequinoktium.kleine.Mondwende.2033.png|mini|rechts|hochkant=1|Herbstäquinoktium (orange) und Sonnenfinsternis bei aufsteigendem (rot) Neumond am 23. September 2033 (orangefarbene Linie), Mondfinsternis bei absteigendem (blau) Vollmond am 8. Oktober 2033 und Wintersonnenwende (türkis) mit kleiner Mondwende bei Neumond am 21. Dezember 2033 (grüne Linie).]]
Der '''Vollmond''' (weiß) erscheint sowohl zu Beginn der Darstellung am 6. Januar 2023 als auch nach genau 19 Jahren am 6. Januar 2042. Dieser Sachverhalt ergibt sich daraus, dass nach dem Durchlauf eines Meton-Zyklus eine ganzzahlige Anzahl von 235 synodischen Perioden durchlaufen wird (gleiche '''Elongation''' des Mondes). Diese Vollmonde stehen bei der gleichen ekliptikalen Länge im Sternbild Zwillinge (Gemini). Dieser Sachverhalt ergibt sich daraus, dass nach dem Durchlauf eines Meton-Zyklus eine ganzzahlige Anzahl von 254 siderischen Perioden durchlaufen wird. Gleichzeitig steht auch die Sonne nach 19 tropischen Sonnenjahren auf dem Zodiak bei der gleichen ekliptikalen Länge und somit erneut genau gegenüber vom Mond im Sternbild Schütze (Sagittarius). Da die Meton-Periode nur gut einen halben Tag länger ist als 255 drakonitische Perioden, stehen diese beiden Vollmonde bei fast der gleichen ekliptikalen Breite nördlich vom Stern Mebsuta (ε Geminorum).
'''Große Mondwenden''' mit maximaler Höhe des Mondes und längstem Bogen über dem Horizont folgen stets dem aufsteigenden Mondknoten (rot), und '''kleine Mondwenden''' mit minimaler Höhe des Mondes und kürzestem Bogen über dem Horizont folgen stets dem absteigenden Mondknoten (blau).
[[Datei:KleinsteMondwende.23.6.2024.HDR.png|mini|hochkant=2|'''Kleinste Mondwende um die Mittsommernacht 2024''': Der Vollmond (scheinbarer Durchmesser = 31,8 Minuten) am 23. Juni 2024 nach ein Uhr und kurz vor der oberen Kulmination auf dem südlichen Meridian von Berlin-Lankwitz (nördliche geographische Breite = 52,44 Bogengrad) aus gesehen. Die ekliptikale Breite hatte einen Minimalwert, der fast 6 Bogengrad südliche Breite betrug und somit zu einer extrem geringen maximalen Höhe über dem Horizont von nur 8,5 Bogengrad führte. Im norwegischen Trondheim (nördliche geographische Breite = 63,44 Bogengrad, also drei Breitengrade südlich vom nördlichen Polarkreis) oder im isländischen Reykjavik (nördliche geographische Breite = 64,15 Bogengrad) war in diesen Tagen weder die Mitternachtssonne noch der Vollmond zu sehen. Der helle Stern rechts oberhalb der Mondscheibe ist Nunki im Schützen (σ Sagittarius, visuell 2,7<sup>m</sup>), unterhalb ist Asella (ζ Sagittarius, visuell 4,2<sup>m</sup>) zu sehen.]]
Die '''größte Mondwende''' bei Vollmond tritt nur zur Wintersonnenwende und die '''kleinste Mondwende''' bei Vollmond tritt nur zur Sommersonnenwende auf. Je nach ekliptikaler Breite des Mondes schwanken die absoluten Werte für die Horizontwinkel um das doppelte der Inklination der Mondbahn, das heißt insgesamt um bis zu 10,29 Bogengrad. Die ekliptikale Breite bezieht sich auf den Erdmittelpunkt, da dieser in der Ekliptikebene liegt. Die von der Erdoberfläche aus beobachtete ekliptikale Breite des im Mittel nur zirka 383100 Kilometer entfernten Mondes hängt zusätzlich vom jeweiligen geographischen Breitengrad der Beobachtung ab. Der in Bezug auf die Ekliptik nördlichste und südlichste Punkt auf der Erdoberfläche liegen um den Erdradius von gut 6350 Kilometer oberhalb (nördlich) beziehungsweise unterhalb (südlich) der Ekliptikebene. Daraus resultiert für die beobachtete ekliptikale Breite ein zusätzlicher Winkel von knapp einem Bogengrad:
:<math>\Delta \beta = \arctan \frac {\pm 6350} {383100} \approx \pm 0,95^\circ</math>
Die '''größte Mondwende bei Vollmond''' (weiß) tritt bei maximaler ekliptikaler Breite des Mondes um die Wintersonnenwende (türkis, siehe Dezember 2023) auf. Die '''kleinste Mondwende bei Vollmond''' (weiß) tritt bei minimaler ekliptikaler Breite des Mondes um die Sommersonnenwende (gelb, siehe Juni 2024).
Im 19-jährigen Diagramm (siehe oben) ist gut zu erkennen, wie sich die Konstellation zwischen Mondknoten (rot und blau) und Mondphasen (weiß und schwarz) nach den 19 Jahren des '''Meton-Zyklus''' wiederholt (vergleiche Januar 2023 und Januar 2042).
Der erste aufsteigende Mondknoten (rot) wird fünf Tage vor Vollmond am 2. Januar 2023 erreicht. Nach einer vollständigen '''Saros-Periode''' von 18 Jahren und 11 Tagen, also am 12. Januar 2041 wird fünf Tage vor Vollmond ebenfalls wieder ein aufsteigender Mondknoten erreicht.
Eine Besonderheit gibt es im März 2038: Die Tag-und-Nacht-Gleiche erfolgt bereits am 20. März 2038 (Samstag) und der erste Frühlingsvollmond nur einige Stunden später am 21. März 2038 (Sonntag). Grundsätzlich müsste also der darauffolgende Sonntag, nämlich der 28. März 2038, das '''Osterdatum''' sein. In diesem Fall greift die abweichende Regel, dass erst der Vollmond nach dem 21. März als Frühlingsvollmond zu rechnen ist, was erst am 19. April 2038 (Montag) der Fall ist. Demzufolge ist das Osterdatum auf den 25. April 2038 (Sonntag) festgelegt.
Eine '''Sonnenfinsternis '''tritt immer dann auf, wenn ein Neumond (weiß) zeitnah durch einen aufsteigenden (rot) oder absteigenden (blau) Mondknoten läuft. Dies ist im obigen Diagramm an den folgenden Tagen der Fall:
* 20. April 2023, 14. Oktober 2023, 8. April 2024, 2. Oktober 2024, 29. März 2025, '''21. September 2025''' (Herbstäquinoktium), 17. Februar 2026, 12. August 2026, 6. Februar 2027, 2. August 2027, 26. Januar 2028, 22. Juli 2028, 14. Januar 2029, 12. Juni 2029, 11. Juli 2029, 5. Dezember 2029, 1. Juni 2030, 25. November 2030, 21. Mai 2031, 14. November 2031, 9. Mai 2032, 3. November 2032, 30. März 2033, '''23. September 2033''' (Herbstäquinoktium), '''20. März 2034''' (Frühlingsäquinoktium), 12. September 2034, 9. März 2035, 2. September 2035, 27. Februar 2036, 23. Juli 2036, 21. August 2036, 16. Januar 2037, 13. Juli 2037, 5. Januar 2038, 2. Juli 2038, 26. Dezember 2038, '''21. Juni 2039''' (Sommersonnenwende), 15. Dezember 2039, 11. Mai 2040, 4. November 2040, 30. April 2041, 25. Oktober 2041
Genau neunzehn Jahre nach den ersten beiden genannten Sonnenfinsternissen im Jahr 2023 treten an den gleichen Kalendertagen erneut zwei Sonnenfinsternisse auf, nämlich am 20. April 2042 und am 14. Oktober 2042.
Neunzehn Jahre vor den sechs ersten aufgeführten Sonnenfinsternissen in den Jahren 2023 bis 2025 traten an fast immer den gleichen Kalendertagen ebenfalls Sonnenfinsternisse auf, nämlich am 19. April 2004, am 14. Oktober 2004, am 8. April 2005, am 3. Oktober 2005, am 29. März 2006 und am 22. September 2006.
Eine '''Mondfinsternis '''tritt immer dann auf, wenn ein Vollmond (weiß) zeitnah durch einen aufsteigenden (rot) oder absteigenden (blau) Mondknoten läuft. Dies ist im obigen Diagramm an den folgenden Tagen der Fall:
* 5. Mai 2023, 28. Oktober 2023, 25. März 2024, 18. September 2024, 14. März 2025, 7. September 2025, 3. März 2026, 28. August 2026, 20. Februar 2027, 18. Juli 2027, 17. August 2027, 12. Januar 2028, 6. Juli 2028, 31. Dezember 2028, 26. Juni 2029, '''20. Dezember 2029''' (Wintersonnenwende), 15. Juni 2030, 9. Dezember 2030, 7. Mai 2031, 5. Juni 2031, 30. Oktober 2031, 25. April 2032, 18. Oktober 2032, 14. April 2033, 8. Oktober 2033, 3. April 2034, 28. September 2034, 22. Februar 2035, 19. August 2035, 11. Februar 2036, 7. August 2036, 31. Januar 2037, 27. Juli 2037, 21. Januar 2038, '''17. Juni 2038''' (Sommersonnenwende), 16. Juli 2038, 11. Dezember 2038, 6. Juni 2039, 30. November 2039, 26. Mai 2040, 18. November 2040, 16. Mai 2041, 8. November 2041
<gallery caption="Finsternisse" perrow="1" widths="1024" heights="470">
Finsternisse.2025.png|Die Finsternisse im Jahr 2025 liegen in der Nähe der Tag-und-Nacht-Gleichen, wo mittlere Mondwenden vorliegen. Der Mond geht im Osten auf und im Westen unter.
Finsternisse.2029.png|Die Finsternisse im Jahr 2029 liegen in der Nähe der Sonnenwenden. Bei der Sonnenfinsternis im Juni kommt es bei Neumond zu einer großen Mondwende, und bei der darauffolgenden Mondfinsternis kommt es bei Vollmond zu einer kleinen Mondwende. Bei der Sonnenfinsternis im Dezember kommt es bei Neumond zu einer kleinen Mondwende, und bei der darauffolgenden Mondfinsternis kommt es bei Vollmond zu einer großen Mondwende.
</gallery>
==Der Kalenderstein vom Tempel Mnajdra==
Auf Malta wurde im Hypogäum von Ħal-Saflieni beim Ort Tarxien ein annähernd kreisrunder Stein aus der Tempelperiode der Insel mit zirka sechs Zentimeter Durchmesser gefunden, der wie die Darstellung einer Vollmondscheibe aussieht.<ref>Daniel Cilia: [http://web.infinito.it/utenti/m/malta_mega_temples/TempleFig/%20Pres,Misc/pages/face.htm Found in a house at Hal Saflieni, stone, c.6 cm wide], The megalithic temples of Malta - the world's most ancient stone architectur, DSCF9754</ref> Im maltesischen Tempel Mnajdra sind an der südlichen Küste Maltas zirka zehn Kilometer entfernt davon zwei große Kalendersteine gefunden worden, die ebenfalls aus dieser Zeit stammen.
<gallery caption="Die Ruinen der Tempelanlage von Mnajdra" widths="300" heights="300" mode="packed">
Plan_der_Tempel_von_Mnajdra.png|Plan der Tempelanlage. Die beiden Kalendersteine befinden sich in der Mitte des Urtempels im Osten der Anlage (Buchstabe "A"), die aus dem vierten vorchristlichen Jahrtausend stammt.
Mini_Europa_Brüssel-2125_Tempel_von_Mnajdra.jpg|Modell der Tempelanlage im Park "Mini-Europa" in Brüssel. Die Kalendersteine befinden sich links und rechts hinter der Frauenfigur auf der rechten Seite.
Malta_-_Qrendi_-_Hagar_Qim_and_Mnajdra_Archaeological_Park_-_Mnajdra_16_ies.jpg|Darstellung der Lochreihen der beiden Kalendersteine auf einer Schautafel der archäologischen Stätte.
Mnajdra_East_Temple_1_(6799974274).jpg|Blick vom Eingang des Osttempels auf die beiden aufrechtstehenden Kalendersteine – links der westliche und rechts der östliche Kalenderstein.
Mnajdra_Temple_-_Chris_Brown.jpg|Blick über die Kalendersteine – links der östliche und rechts der westliche Kalenderstein, den Eingang des Osttempels und die Südküste Maltas auf das Mittelmeer mit der vorgelagerten Insel Filfla.
</gallery>
Auf dem östlichen Kalenderstein gibt es mehrere Lochreihen, deren Lochzahlen alle mit lunaren und solaren Kalendern in Zusammenhang gebracht werden können. In der heute zusammengestellten Anordnung der Steine können nicht alle Löcher gebohrt worden sein, da diese teilweise von anderen Steinen verdeckt sind. Die Bohrungen sind heute in horizontaler Richtung ausgerichtet, wurden damals vermutlich unter Ausnutzung der Gravitation senkrecht nach unten auf dem noch liegenden Stein ausgeführt. In dieser Ausrichtung des Steins wäre es auch leicht möglich gewesen, für Markierungs- oder Zählzwecke zum Beispiel kugelförmige Gegenstände in die Löcher zu legen. Steckkalender aus Stein mit Bohrungen (bei den Griechen hießen sie '''Parapegmata''') waren im Altertum an vielen Orten in Verwendung.
Am Kopf des Steins gibt es mehrere hundert, flächenhaft angeordnete Löcher, die eventuell für die einzelnen Monate oder Jahre einer langfristigen Beobachtung stehen. Darunter tauchen rechtsbündig sieben horizontale Lochreihen auf, die in der Skizze mit den Buchstaben A bis G gekennzeichnet sind, wobei die beiden Teilreihengruppen B1 und B2 sowie C1, C2 und C3 zusammengefasst betrachtet werden:
[[Datei:Kalenderstein.Mnajdra.labelled.png|mini|links|hochkant=3|Skizze der Lochreihen auf dem Kalenderstein von Mnajdra nach Ventura und Hoskin.<ref name="Ventura">Frank Ventura, Michael Hoskin: [[doi:10.1007/978-1-4614-6141-8_133|Temples of Malta]], in: Clive Ruggles (Herausgeber), ''Handbook of Archaeoastronomy and Ethnoastronomy'', 7. Juli 2014, Seiten 1421-1430, Springer, New York, ISBN 978-1-4614-6140-1</ref>]]
{| class="wikitable"
|+ Lochreihen auf dem östlichen Kalenderstein vom Tempel Mnajdra auf Malta
!title="Reihe"| Reihe
!title="Anzahl der Löcher"| Anzahl der Löcher
!title="Mögliche Verwendung"| Mögliche Verwendung
|-
| A || 19 || Für die jeweilige Goldene Zahl jedes Sonnenjahres innerhalb des 19-jährigen '''Meton-Zyklus''' (235 synodische, 255 drakonitische, 254 siderische Monate beziehungsweise 6940 Tage).<br/>Nach einem Sonnenjahr hat die Sonne wieder die gleiche ekliptikalen Länge. Nach Ablauf der gesamten Meton-Periode hat der Mond wieder die gleiche Mondphase '''und''' die gleiche ekliptikalen Breite '''und''' die gleiche ekliptikalen Länge (zum Beispiel im Goldenen Tor der Ekliptik oder im Frühlingspunkt).
|-
| rowspan=2 | B || B<sub>1</sub>: 13 (links) || rowspan=2 | In Summe 29, für die Anzahl der vollständigen Tage in einem '''synodischen Monat''' (29,5 Tage). Nach dieser Zeit hat der Mond wieder die gleiche Mondphase erreicht.<br/>Vom Altlicht des Mondes bis zum Vollmond sind es 16 Tage, und danach sind es 13 Tage bis zum nächsten Altlicht.<br/>Nachdem die Doppelreihe vervollständigt wurde, gibt es dafür einen Übertrag in die Reihe E und wenn diese bereits voll ist, für das nächste beginnende Jahr mit der nächstfolgenden '''Goldenen Zahl''' einen Übertrag in die Reihe A.
|-
| B<sub>2</sub>: 16 (rechts darunter)
|-
| rowspan=3 | C || C<sub>1</sub>: 3 (rechts oben) || rowspan=2 | Für die sieben vollständigen Tage eines '''Mondviertels''' (≈7,4 Tage) respektive einer '''Woche'''.<br/>Wenn diese Doppelreihe gefüllt ist, gibt es für die Vervollständigung einer neuen Woche einen Übertrag in die Reihe G für die Wochen in einem Jahr.<br/>Alternativ könnten hier jeweils die drei Monate in den vier Jahreszeiten markiert und gezählt worden sein.<br/>Ein weiterer Zusammenhang besteht eventuell mit der Tatsache, dass in 19 Jahren (siehe Reihe A) sieben synodische Schaltmonate erforderlich sind, um in einem lunisolaren Kalender den Lauf von Sonne und Mond bis auf wenige Stunden genau zu synchronisieren (siehe hierzu auch''' [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Plejaden#Schaltregeln|Plejaden-Schaltregeln]]''').
|-
| C<sub>2</sub>: 4 (rechts unten)
|-
| C<sub>3</sub>: 3 (links) || Für die drei nach Neumond '''vollendeten Mondviertel''' innerhalb eines laufenden synodischen Monats.</br>Beim Erreichen eines Neumonds, eines abnehmenden Halbmonds, eines Vollmonds oder eines abnehmenden Halbmonds gibt es jeweils einen Übertrag in die Reihe D oder in die Reihe F (siehe unten).
|-
| D || 25 || Für die 25 '''vollendeten Mondviertel''' in der ersten Hälfte eines Sonnenjahres, oder entweder für alle '''zunehmenden Mondviertel''' (wenn der Mond zu Beginn des Jahres zunehmend war) oder für alle '''abnehmenden Mondviertel''' (wenn der Mond zu Beginn des Jahres abnehmend war) eines Sonnenjahres (vergleiche Reihe F).
|-
| E || 11 || Nachdem die Doppelreihe B vollständig durchlaufen wurde, gibt es einen Übertrag in diese Reihe. Diese elf Mulden stehen dann für '''überzähligen Tage''' in einem Sonnenjahr (365,2 Tage) im Vergleich zu zwölf synodischen Monaten (354,4 Tage). Wenn diese Reihe bereits voll ist, gibt es für das nächste beginnende Jahr mit der nächstfolgenden '''Goldenen Zahl''' einen Übertrag in die Reihe A.
|-
| F || 24 + 1 = 25 || Für die 24 bis 25 '''vollendeten Mondviertel''' in der zweiten Hälfte eines Sonnenjahres, oder entweder für alle '''abnehmenden Mondviertel''' (wenn der Mond zu Beginn des Jahres zunehmend war) oder für alle '''zunehmenden Mondviertel''' (wenn der Mond zu Beginn des Jahres abnehmend war) eines Sonnenjahres (vergleiche Reihe D). Das 25. Loch ist etwas abgesetzt, da es für ein am Ende des Jahres eingeschaltetes 50. Mondviertel (Dauer = 7,38265 Tage) steht, das nur in ungefähr jedem zweiten Sonnenjahr auftritt:<br/>
:49 x 7,38265 Tage ≈ 361,75 Tage beziehungsweise 50 x 7,38265 Tage ≈ 369,13 Tage<br/>
:365,242 Tage - 361,75 Tage ≈ 3,5 Tage beziehungsweise 365,242 Tage - 369,13 Tage ≈ -3,9 Tage
|-
| G || 53 || Für die begonnenen 53 '''Siebentagewochen''' in einem Sonnenjahr (Dauer = 365,242 Tage) beziehungsweise von einem heliakischen Auf- oder akronychischen Untergang der Plejaden zum nächsten.
|}
[[Datei:Altlicht.16.9.2020.P1079087.jpg|mini|rechts|Über dem östlichen Horizont beim Morgenletzt gerade noch sichtbares Altlicht des abnehmenden Mondes 33 Stunden vor Neumond mit der vom Erdschein beleuchteten Nachtseite des Mondes. Die Aufnahme entstand kurz vor Herbstbeginn, als die Ekliptik morgens fast senkrecht auf dem Horizont stand.]]
Zu der Doppelreihe B sei angemerkt, dass auch im altägyptischen Mondkalender, der im Neolithikum in Verwendung war, der Monat nicht mit dem unsichtbaren Neumond, sondern mit dem gerade noch sichtbaren Altlicht des Morgenletztes des Mondes begann, also gut einen Tag vor Neumond.<ref>Joachim Friedrich Quack: [https://core.ac.uk/download/pdf/35120251.pdf Zwischen Sonne und Mond - Zeitrechnung im Alten Ägypten], Seite 38, in: Harry Falk (Herausgeber), ''Vom Herrscher zur Dynastie. Zum Wesen kontinuierlicher Zeitrechnung in Antike und Gegenwart'', Bremen 2002</ref> Die beiden letzten Löcher sind etwas nach links abgesetzt, was mit folgendem Sachverhalt im Einklang steht: zwei Tage vor dem Ende einer synodischen Periode, also schon nach gut 27 Tagen, ist ein siderischer Monat vorüber, nach welchem der Mond die gleiche ekliptikale Länge erreicht hat. Das heißt bereits nach gut 27 Tagen steht der Mond zum Beispiel wieder im Goldenen Tor der Ekliptik, bevor er erst nach gut 29 Tagen erneut sein Altlicht erreicht (gut einen Tag vor Neumond). Die Sonne ist innerhalb des synodischen Monats durch die Bewegung der Erde um die Sonne gegenüber dem Fixsternhimmel um knapp 30 Bogengrad weiter nach links gezogen.
Die 25 Wochen des abnehmenden Mondes werden zum Beispiel auch im 79. Kapitel des ungefähr 2000 Jahre alten {{w|Äthiopisches Henochbuch|äthiopischen Henochbuches}} erwähnt.<ref>Siehe auch {{s|Henochbuch_(oder_Erster_Henoch)#Der_dritte_Teil_72–82_Das_astronomische_Buch|Wikisource, Henochbuch, Teil 3, Kapitel 71 bis 82}}</ref> Alternativ könnten die 50 Löcher in Reihen D und F eventuell auch für die 50 vollständigen Siebentagewochen (350 Tage) innerhalb von zwölf synodischen Perioden stehen, die eine Dauer von 50,6 Wochen beziehungsweise 354,4 Tagen haben.
Zur Zahl Elf (Reihe E) ist noch festzuhalten, dass die Erde innerhalb eines siderischen Jahres des Planeten Jupiter (zwölf Erdenjahre) elf Mal mit diesem in Opposition steht. Zu diesen Zeitpunkten ist der Abstand zwischen Erde und Jupiter am geringsten, der Jupiter hat steht in seinem größten Glanz und er kulminiert um Mitternacht auf dem südlichen Meridian.
[[Datei:Mnajdra East Temple 2 (6946102161).jpg|rechts|mini|Detail des westlichen Kalendersteins aus dem Osttempel von Mnajdra.]]
Auch auf einem weiteren, sogenannten westlichen und heute ebenfalls aufgerichteten Stein der Tempelanlage sind mehrere Lochreihen zu sehen, die aus 16, 12, 19, 7, 30, 31, 32, 35, 37, 12 und 13 Löchern bestehen.<ref>David Humiston Kelley, Eugene Frank Milone: ''Exploring Ancient Skies: A Survey of Ancient and Cultural Astronomy'', Part II ''Astronomy in Cultures'', 6 ''Paleolithic and Neolithic Cultures'', 6.2 ''Megalithic Cultures'', 6.2.18 ''Mediterranean and North African Megalithic Sites'', 6.2.18.1 ''Malta'', pages 201 and 202, Springer, 2011, ISBN 9781441976246</ref> Einige dieser Zahlen tauchen auch im Zusammenhang mit dem östlichen Stein auf oder sind ebenfalls leicht mit lunaren oder solaren Kalendertagen in Verbindung zu bringen:
* 16: Anzahl der Tage vom Altlicht bis zum Vollmond.
* 12: Anzahl der vollständigen Monate pro Jahr.
* 19: Anzahl der tropischen Jahre pro Meton-Periode.
* 7: Anzahl der vollständigen Tage pro Mondviertel respektive der Tage pro Woche.
* 30: aufgerundete Anzahl der Tage pro synodische Periode respektive der Tage pro Monat.
→ In Bezug auf die Bedeutung von bestimmten Zahlen in der Astronomie siehe auch '''[[Quadriviale Kuriositäten/ Zahlen|Exkurs „Zahlen“]]'''.
<div style="clear:both"></div>
==Malereien in der Höhle von Magura==
In der schon im Neolithikum genutzten Magura-Höhle im Nordwesten des heutigen Bulgariens gibt es nicht nur eine sehr alte bildliche Darstellung eines Schöpfungsmythos, sondern ebenfalls Hinweise darauf, dass verschiedene Mondzyklen bekannt waren. Unter den Darstellungen befindet sich insbesondere eine Reihe von Strichen, mit denen die 16 Tage vom Altlicht des Mondes bis zu Vollmond gezählt worden sein können.
→ Weitere Erläuterungen finden sich im '''[[Die_Höhlenmalerei_in_der_Magura-Höhle|Wikibook „Die Höhlenmalerei in der Magura-Höhle“]]'''.
[[Datei:Magura.Altlicht.Vollmond.png|links|mini|hochkant=4|Erste Hälfte des synodischen Monats in einer Darstellung der Höhlenmalerei von Magura. Rechts ist die schmale, liegende Mondsichel des Altlichts beim Morgenletzt zu sehen. Ein bis zwei Tage später ist Neumond, danach nimmt der Mond wieder zu, und nach insgesamt sechzehn Tagen wird der Vollmond erreicht (links).]]
<div style="clear:both"></div>
== Kontrastverhältnisse bei aschgrauem Mondlicht ==
[[Datei:Aschgraues.Mondlicht.P1162513.jpg|mini|hochkant=2|Die vom Erdschein beleuchtete Nachtseite des Mondes wird neben der schmalen Mondsichel (11 Prozent, Mondalter = 3,3 Tage) mit ''aschgrauem Mondlicht'' sichtbar. Aufnahme über dem westlichen Abendhimmel kurz nach dem Äquinoktium im Frühjahr bei besonders steiler Ekliptik von Berlin aus gesehen. Links der Stern Omikron Arietis (37 Ari, 6,2<sup>m</sup>) im Sternbild Widder.<br/><br/>
Mit bloßen Augen ist das aschgraue Licht im Mondschatten nur unter sehr guten atmosphärischen Bedingungen und mit voller menschlicher Sehkraft gerade noch erkennbar. Der Mond hat nur einen Winkeldurchmesser von 0,5 Bogengrad, was bei ausgestrecktem Arm nur ungefähr einem Viertel der Breite eines Fingers entspricht. Die hier gezeigte Aufnahme ist sehr hell belichtet, so dass die Kontraste im Mondschatten einfacher wahrzunehmen sind als in der Natur:
[[Datei:Aschgraues.Mondlicht.P1162513.jpg|90px|zentriert]]
]]
Das '''aschgraue Mondlicht''' kann wenige Tage vor und nach Neumond im Schattenbereich des Mondes beobachtet werden, wenn die Sonne unter dem Horizont steht. Der Schattenbereich wird hierbei durch den '''Erdschein''' beleuchtet und hebt sich deutlich sichtbar vom Himmel ab, der den Mond umgibt. Der Erdschein hängt vom Albedo der vom direkten Sonnenlicht getroffenen Erde ab und kann insbesondere durch den Anteil an Wolken in der Atmosphäre sowie an Eis- und Schneeflächen auf der Erdoberfläche schwanken.
=== Historische Beobachtungen ===
Bereits die Babylonier haben im sechsten vorchristlichen Jahrhundert diesen visuellen Effekt in bildlichen Darstellungen auf Steinreliefs mit dem König Nabonid vor der Mondsichel mit aschgrauem Licht festgehalten:<ref>Friedhelm Pedde: ''[https://wfs.berlin/wp-content/uploads/2021/09/BROplanet-12-okt2021ff_www.pdf Götter und Planeten im Alten Orient - Sin und der Mond]'', Mitteilungen, Ausgabe 12, Seite 6 und 7, Oktober 2021, Wilhelm-Foerster-Sternwarte e.V. / Zeiss-Planetarium am Insulaner</ref>
<gallery caption="" mode="packed" widths="600" heights="400">
Urfa museum King Nabonid embossed inscription Iron age - 5026.jpg|Darstellung des letzten Königs des Neubabylonischen Reiches, Nabonid, vor der Mondsichel mit aschgrauem Mondlicht am Kopf einer babylonischen Stele aus Harran im Archäologischen Museum Şanlıurfa in der Türkei aus dem sechsten vorchristlichen Jahrhundert.
Nabonidus.jpg|Darstellung des letzten Königs des Neubabylonischen Reiches, Nabonid, vor der Mondsichel mit aschgrauem Mondlicht am Kopf einer babylonischen Stele im Britischen Museum.
Voynich.68r.Mondsichel.Erdschein.png|Darstellung der Mondsichel mit einem Gesicht im Schattenbereich des Mondes im astronomischen Abschnitt des Voynich-Manuskripts aus dem 15. Jahrhundert.
Caspar David Friedrich - Zwei Männer in Betrachtung des Mondes.jpg|Zwei Männer am Abend bei der Betrachtung des Neulichts von Caspar David Friedrich (1819/1820), Öl auf Leinwand, Albertinum, Dresden. Eine in dieser Darstellung ähnliche Konstellation zwischen einer solchen Mondsichel und dem Planeten Jupiter fand am Abend des 28. Märzes 1811 im Sternbild Stier (Taurus) statt. Eine Woche nach der Tag-und-Nacht-Gleiche des Frühjahrs stand der Mond wie im Bild dargestellt mit stark geneigter Sichel etwas tiefer als der Jupiter über dem westlichen Horizont.
Moon - Artemis II - April 6 2026 (55193552739).jpg|Die Aufnahme der Artemis-II-Besatzung während ihres Mondvorbeiflugs am 6. April 2026 zeigt eine Sonnenfinsternis. Am linken Rand der Mondscheibe ist das schwache Leuchten des aschgrauen Mondlichts zu erkennen, das vom Erdschein der links vom Mond befindlichen Erde hervorgerufen wurde.
</gallery>
=== Bestimmung der Kontraste ===
[[Datei:Altlicht.Mond.P1116624.jpg|mini|rechts|hochkant=2|Altlicht des Mondes am 3. November 2021 mit aschgrauem Licht im Schattenbereich des Mondes von Berlin aus gesehen.]]
Das nebenstehende Bild des Altlichts (Mondalter 27,7 von 29,5 Tagen) im Sternbild Jungfrau wurde am 3. November 2021 um 7:05 Uhr morgens mit einem hochwertigen Teleobjektiv mit guter Kontrastübertragung über dem ostsüdöstlichen Horizont (Azimut 122 Bogengrad) in Berlin aufgenommen. Die Sonne stand zum Zeitpunkt der Aufnahme noch gut ein Bogengrad unter dem Horizont, der Mond hatte eine nördliche ekliptikale Breite von gut 3 Bogengrad und eine Elongation von 22 Bogengrad, so dass er bei wolkenlosem Himmel und bei einer Höhe von 17,5 Bogengrad über dem Horizont gut zu beobachten war. Die Mondscheibe war mit bloßem Auge vollständig sichtbar und war zu 3,7 Prozent vom direkten Sonnenlicht beleuchtet.
Die aufgenommenen Rohdaten wurden für diese Auswertung mit einer Software mit möglichst neutralen Einstellungen zu einer Schwarz-Weiß-Aufnahme entwickelt (Kontrast = 1, Gamma = 1, Farbtemperatur = 4100 Kelvin, Farbsättigung = 0). Anhand der Bilddaten können die Luminanzwerte (Helligkeiten) im Bild bestimmt werden, um aus beliebigen paarweisen Luminanzwerten den jeweiligen Kontrast zu berechnen. Der '''Michelson-Kontrast''' <math>M</math> (auch '''Modulation'''), der sich aus zwei benachbarten Luminanzwerten <math>L_2</math> und <math>L_1</math> ergibt (Bedingung: <math>L_2 \ge L_1 \ge 0</math>), liegt immer zwischen 0 (<math>L_2 = L_1</math>, also kein Kontrast) und 1 beziehungsweise 100 Prozent (<math>L_1 = 0</math>, also maximaler Kontrast):
:<math>M = \frac {L_2 - L_1} {L_2 + L_1}</math>
→ Siehe auch [[Digitale_bildgebende_Verfahren:_Grundlagen#Modulation|Wikibook '''Digitale bildgebende Verfahren''', Kapitel '''Grundlagen''', Abschnitt '''Modulation''']].
Die Sichel ist in der ausgewerteten Aufnahme nicht überbelichtet, hat im Scheitel einen mittleren Luminanzwert von:
:<math>L_{Sichel} = 236</math>
Der den Mond umgebende Himmel hat durch das vorhandene Streulicht in der Troposphäre einen mittleren Luminanzwert von:
:<math>L_{Himmel} = 15</math>
Daraus resultiert ein entsprechend hoher Michelson-Kontrast <math>M</math>:
:<math>M_{Sichel,Himmel} = \frac {L_{Sichel} - L_{Himmel}} {L_{Sichel} + L_{Himmel}} = \frac {236 - 15} {236 + 15} = \frac {221} {251} = 0,88</math>
Im aschgrauen Licht in der Schattenseite des abnehmenden Mondes (dessen gesamte scheinbare Helligkeit beträgt bei der in der Abbildung sichtbaren Mondphase übrigens ungefähr -3<sup>m</sup>), die vom Erdschein beleuchtet wird, ergibt sich für die etwas variierende Reflektivität der Mondoberfläche ein mittlerer Luminanzwert von:
:<math>L_{Schatten} = 24,5</math>
Im Vergleich zur hellen Mondsichel ergibt sich daraus ein etwas geringerer Kontrast zwischen Mondsichel und Mondschatten als zwischen Mondsichel und Nachthimmel:
:<math>M_{Sichel,Schatten} = \frac {L_{Sichel} - L_{Schatten}} {L_{Sichel} + L_{Schatten}} = \frac {236 - 24,5} {236 + 24,5} = \frac {211,5} {260,5} = 0,81</math>
Die Standardabweichung beträgt in der Schattenseite des abnehmenden Mondes:
:<math>L_{Abweichung} = 2,5</math>.
Innerhalb der sichtbaren Schattenseite des Mondes resultiert aus den dort mit einer Standardabweichung um den Mittelwert herum auftretenden Luminanzwerten für das aschgraue Licht ein Kontrast von nur 10 Prozent:
:<math>M_{Schatten} = \frac {(L_{Schatten} + L_{Abweichung}) - (L_{Schatten} - L_{Abweichung})} {(L_{Schatten} + L_{Abweichung}) + (L_{Schatten} - L_{Abweichung})} = \frac {L_{Abweichung}} {L_{Schatten}}= \frac {2,5} {24,5} = 0,10</math>
Dieser Kontrastwert ergibt sich im Übrigen im gleichen Maße auch auf der von der Sonne voll beleuchteten und dann jedoch insgesamt erheblich helleren Oberfläche des Vollmonds (die scheinbare Helligkeit beträgt dann ungefähr -13<sup>m</sup>).
:'''Anmerkung''': ''Die Sinnhaftigkeit der Kontrastbestimmung mit der Standardabweichung begründet sich in der Tatsache, dass eine perfekte schwarz-weiße Kante mit einem maximalen Luminanzwert <math>L_{max}</math> im hellen Bereich und einem Luminanzwert von Null im dunklen Bereich eine Modulation <math>M</math> von eins hat, sofern die weiße und die schwarze Teilfläche gleich groß sind und jeweils <math>\frac {n} {2}</math> Messwerte beinhalten. Sowohl der Mittelwert aller <math>n</math> Luminanzwerte als auch die Standardabweichung aller <math>n</math> Luminanzwerte sind dann beide genau halb so groß wie der maximale Luminanzwert:''
::<math>L_{Mittelwert} = \sum_{i=1}^n L_{i} = \sum_{i=1}^{\frac {n} {2}} 0 + \sum_{i=1}^{\frac {n} {2}} L_{max} = \frac {L_{max}} {2}</math>
::<math>L_{Standardabweichung} = \sqrt {\frac {\sum_{i=1}^n (L_{i} - L_{Mittelwert})^2} {n}} = \sqrt {\frac {\sum_{i=1}^n \left( \frac {L_{max}} {2} \right)^2} {n}} = \sqrt {\frac {n \cdot \left( \frac {L_{max}} {2} \right)^2} {n}} = \frac {L_{max}} {2}</math>
:''Der Mittelwert zuzüglich der Standardabweichung entspricht dann dem maximalen Luminanzwert:''
::<math>L_{Mittelwert} + L_{Standardabweichung} = \frac {L_{max}} {2} + \frac {L_{max}} {2} = L_{max}</math>
:''Und der Mittelwert abzüglich der Standardabweichung hat dann den Wert Null:''
::<math>L_{Mittelwert} - L_{Standardabweichung} = \frac {L_{max}} {2} - \frac {L_{max}} {2} = 0</math>
:''Der auf diesem Weg bestimmte Kontrast stimmt mit dem oben genannten Kontrast an der Kante überein:''
::<math>M = \frac {L_{max} - 0} {L_{max} + 0} = \frac {L_{max}} {L_{max}} = 1</math>
Schließlich kann auch noch der Kontrast an der Außenkante der nur vom Erdschein beleuchteten Mondscheibe bestimmt werden, also zwischen aschgrauem Mondlicht und Himmel:
:<math>M_{Schatten,Himmel} = \frac {L_{Schatten} - L_{Himmel}} {L_{Schatten} + L_{Himmel}} = \frac {24,5 - 15} {24,5 + 15} = \frac {9,5} {39,5} = 0,24</math>
Dieser Kontrast ist also deutlich höher als der innerhalb des Mondschattenbereichs. Die Helligkeit wechselt an der Kante der Mondscheibe zudem schlagartig und ist deswegen besser wahrnehmbar als die Helligkeitsunterschiede innerhalb des Schattenbereiches, wie im Folgenden ausgeführt wird.
=== Wahrnehmungsphysiologie ===
[[Datei:CSF.against.angular.frequency.in.cycles.per.degree.png|mini|rechts|hochkant=2|Kontrastempfindlichkeitsfunktion (englisch: Contrast Sensitivity Function, CSF) des menschlichen Auges über der Ortsfrequenz <math>f_a</math> in Linienpaaren pro Grad.]]
Strukturen, bei denen sich die Helligkeit innerhalb eines achtel Bogengrades (7,5 Bogenminuten) ändert, werden von einem gesunden menschlichen Auge nahezu perfekt auf die Netzhaut übertragen und sind deshalb am besten wahrzunehmen. Weichere Strukturen mit sich langsamer und kontinuierlich ändernder Helligkeit oder kleinere Strukturen sind bei gleichem Objektkontrast schwieriger zu erkennen. Ob ein bestimmter Objektkontrast mit bloßem Auge wahrgenommen werden kann, kann mit Hilfe der Kontrastempfindlichkeitsfunktion (Contrast Sensitivity Function (CSF)) des menschlichen Auges ermittelt werden, bei der die Kontrastübertragung von einem Objekt auf die Netzhaut des Auges über der Ortsfrequenz in Linienpaaren pro Bogengrad angegeben ist:
→ Siehe auch [[Digitale_bildgebende_Verfahren:_Grundlagen#Kontrastempfindlichkeitsfunktion|Wikibook '''Digitale bildgebende Verfahren''', Kapitel '''Grundlagen''', Abschnitt '''Kontrastempfindlichkeitsfunktion''']].
[[Datei:CSF.UHD.png|mini|rechts|hochkant=2|Zur Kontrastempfindlichkeitsfunktion des menschlichen Auges mit den Ortsfrequenzen von 2 sinusmodulierten Linienpaaren pro Bildbreite (linke Bildhälfte) bis hin zu 1024 Linienpaaren pro Bildbreite (rechts am Bildrand), Bildgröße 3860 mal 2160 Bildpunkte (16:9) mit maximalem Kontrast unten, ohne Kontrast oben.]]
Das nebenstehende Diagramm und die darunter befindliche unfarbige Graphik mit den sinusförmigen Helligkeitsmodulationen visualisieren dies. Von links nach rechts wächst die Ortsfrequenz ausgehend von zwei Linienpaaren pro Bildbreite immer weiter, und der Kontrast ändert sich in vertikaler Richtung kontinuierlich zwischen den Extremwerten 0 und 1. Bei großen und sehr kleinen Ortsfrequenzen sind geringe Kontraste nicht so gut zu erkennen wie im mittleren Bereich. Die maximale Empfindlichkeit wird bei acht Linienpaaren pro Bogengrad erreicht und hängt bei einem gegebenen Objekt demzufolge vom Betrachtungsabstand ab.
Das Auge kann nach dieser Kontrastempfindlichkeitsfunktion innerhalb eines Bogengrades maximal bis zu 80 Linien mit maximalem Objektkontrast (also 40 Linienpaare mit dem Kontrast 1) wahrnehmen. In diesem Fall liegt der wahrgenommene Kontrast zwischen den benachbarten Linien bei ungefähr 5 Prozent. Bei 120 Linien mit maximalem Objektkontrast (also 60 Linienpaare mit dem Kontrast 1) sinkt der wahrgenommene Kontrast auf unter ein Prozent und ist somit praktisch nicht mehr wahrnehmbar. Wegen des scheinbaren Winkeldurchmessers der Mondscheibe von 0,5 Bogengrad, kann das Auge innerhalb des Monddurchmessers also maximal 20 Linienpaare beziehungsweise 40 Linien unterscheiden. Insbesondere an der scharfen Kante der Mondscheibe ist eine ins Auge nahezu verlustfrei übertragene Ortfrequenz von acht Linienpaaren pro Bogengrad stark vertreten, so dass der in der obigen Aufnahme des Mondes vorhandene Objektkontrast von 24 Prozent zwischen Mondschatten und Himmel (siehe oben) gut wahrgenommen werden kann.
Etwas schwieriger gestaltet sich dies innerhalb des Schattenbereichs des Mondes. In der obigen Aufnahme tauchen die stärksten Kontraste entlang einer Linie von oben nach unten auf, wo die helleren Mondebenen mit den Kratern Copernicus und Tycho durch die dunkleren Maria Imbrium und Nubium unterbrochen werden. Daraus resultiert eine Ortsfrequenz mit fünf Linienpaaren pro Bogengrad, wo die Kontrastempfindlichkeitsfunktion ebenfalls einen recht hohen Wert von 0,9 beziehungsweise 90 Prozent erreicht. Dies schwächt den auf der Mondoberfläche vorhandenen Objektkontrast von 10 Prozent im vom Auge auf die Netzhaut übertragenen Bildkontrast nur geringfügig ab.
Aus diesen Überlegungen resultiert, dass die im aschgrauen Licht des Mondes wahrnehmbaren Kontraste knapp 10 Prozent betragen. Diese sind zwar geringer als der Kontrast von 24 Prozent zwischen dem aschgrauen Mondlicht und dem den Mond umgebenden Himmel, sie liegen allerdings noch in einem wahrnehmbaren Bereich. Somit ist erwiesen, dass ein gesundes Auge unter idealen Beobachtungsbedingungen auch völlig ohne optische Hilfsmittel die Helligkeitsunterschiede und Strukturen im nur durch den Erdschein erhellten Schattenbereich des Mondes erkennen kann.
Es gibt jedoch zahlreiche Faktoren, die den wahrnehmbaren Kontrast deutlich herabsetzen oder die Möglichkeit der Wahrnehmung verhindern können. Je älter ein Mensch wird, desto mehr Streulicht wird durch kleinste Verletzungen beziehungsweise Narben in der Hornhaut oder in der Linse des Auges erzeugt. Dieses Streulicht erzeugt einen mehr oder weniger gleichmäßigen Lichtschleier, der auch die dunklen Stellen auf der Netzhaut beleuchtet. Dies führt in jedem Fall zu einer Herabsetzung des wahrnehmbaren Kontrastes und verhindert somit das Erkennen von schwachen Kontrasten.
=== Begrenzende Einflüsse ===
Aber auch in der Natur gibt es viele begrenzende Faktoren. Hierzu gehört das natürliche Streulicht durch die Sonne in der Troposphäre, wenn diese zu dicht am Mond oder zu dicht unter dem Horizont oder gar über dem Horizont steht. In bewohnten Gegenden kommt als großer Störfaktor noch die Lichtverschmutzung hinzu. Ferner wird der Objektkontrast durch atmosphärische Störungen vermindert. Diese ergeben sich zum einen durch Dunst, Nebel oder Staub in der Atmosphäre, und zum anderen aus den langen Wegstrecken, die das Licht bei einer Beobachtung eines Himmelsobjekts durch die Troposphäre zurücklegen muss, wenn dieses sehr dicht über dem Horizont steht. Hierbei wird das den Beobachter erreichende Licht nicht nur abgeschwächt ('''Extinktion'''), sondern auch durch das Licht aller möglichen anderen Lichtquellen angereichert, das an den Luftmolekülen gestreut oder durch Mikroturbulenzen abgelenkt wurde, so dass sich die minimal vorhandene Helligkeit vergrößert und der Kontrast somit verringert. In bewohnten Regionen ist zudem mit erheblicher Lichtverschmutzung durch künstliche Lichtquellen zu rechnen.
Auf der anderen Seite wird insbesondere in Horizontnähe der blaue Lichtanteil von astronomischen Objekten auf dem Lichtweg durch die Troposphäre durch die stark wellenlängenabhängige '''Rayleigh-Streuung''' seitlich abgelenkt, so dass es praktisch gar nicht mehr zum Beobachter gelangt. Dies führt zu den rötlichen Auf- und Untergängen von Sonne und Mond sowie zur Blaufärbung des Taghimmels.
Unter Umständen muss das Licht mehrere hundert Kilometer durch die Atmosphäre zurücklegen, wie in der folgenden Abbildung zu erkennen ist:
[[Datei:Troposphaerische.Laengen.png|mini|zentriert|hochkant=4|Weglängen für Lichtstrahlen in der 15 Kilometer hohen Troposphäre in Abhängigkeit von der Zenitdistanz.]]
=== Beobachtung ===
Die Beobachtungsmöglichkeiten des aschgrauen Lichts werden grundsätzlich durch die folgenden, sich teilweise widerstrebenden Bedingungen verbessert:
* Die '''Lichtverschmutzung''' in der Atmosphäre ist gering.
* Das '''Mondalter''' beträgt wenige Tage vor oder nach Neumond.
* Die '''Sonne''' steht möglichst weit unter dem Horizont.
* Der '''Mond''' hat eine möglichst große Höhe über dem Horizont.
* Die '''Mondsichel''' ist möglichst klein, um möglichst wenig Streulicht zu verursachen.
Für die Beobachtung aus nördlichen geographischen Breiten gilt insbesondere:
* Der Mond hat eine möglichst große '''nördliche ekliptikale Breite''' zwischen seinem aufsteigenden und seinem absteigenden Knoten.
* Die '''Ekliptik''' steht möglichst steil auf dem Horizont. Dies ist bei den Tag-und-Nacht-Gleichen der Fall, besonders gut
** abends im Frühjahr beim '''Neulicht''' des Mondes nach Sonnenuntergang im Westen.
** morgens im Herbst beim '''Altlicht''' des Mondes vor Sonnenaufgang im Osten.
=== Farbigkeit ===
[[Datei:Neulicht.21.3.2026.HDR.1024.png|mini|hochkant=2|Die Abbildung zeigt eine Hochkontrastaufnahme des Erdscheins im Mondschatten zum Frühlingbeginn drei Tage nach Neumond mit Sternen bis zur zehnten Größenklasse (10<sup>m</sup>) in der Umgebung. Die Sichel zeigte einen Tag nach der Tag-und-Nacht-Gleiche im Frühjahr 2026 und zwei Tage vor der großen Mondwende bei einer nördlichen ekliptikalen Breite des Mondes von vier Bogengrad eine besonders starke Neigung. Die Sonne war zuvor im Westen untergegangen und stand bereits elf Bogengrad unter dem Horizont. Die Symmetrieachse der Mondsichel (-7<sup>m</sup>) auf der zu 9,3 Prozent beleuchteten Mondscheibe zeigt nach rechts unten in Richtung der Sonne (Farbtemperatur = 4100 Kelvin, Belichtungsreihe mit sieben Bildern in ganzen Blendenstufen).]]
Bei genauer Betrachtung und Beobachtung ist das sogenannte aschgraue Licht gar nicht farbneutral, sondern spiegelt einen mittleren Farbton der von der Sonne beleuchteten Erdoberfläche wider, von der wiederum der Mondschatten beleuchtet wird. Je niedriger der Bewölkungsgrad, desto stärker ist zudem der Einfluss der farbigen Oberfläche der Erde. Die Farben der Erdoberfläche schwanken hierbei deutlich zwischen den Ozeanen, Wäldern oder Wüsten. In der nebenstehenden Hochkontrastaufnahme zeigt der Mondschatten im HSV-Farbraum einen mittleren Farbwert mit dem Farbwinkel 202°, was als Farbton blaucyan übersetzt werden kann. Dies entspricht recht gut dem mittleren Farbton der der Sonne zugewandten Seite der Erde ("Blue Marble") während der Aufnahme, der für das hier gezeigte Beispielbild im HSV-Farbraum einen mittleren Farbwert mit dem Farbwinkel 201° hat. Der Farbwinkel ist von der Sättigung oder der Helligkeit nicht abhängig.
[[Datei:Neulicht.21.3.2026.HDR.Original.Durchschnitt.gesaettigt.aufgehellt.png|mini|links|hochkant=4|Sichtbarmachung des Farbwerts mit dem Farbwinkel 202° des aschgrauen Mondlichts in einer Hochkontrastaufnahme des Erdscheins im Mondschatten zum Frühlingbeginn drei Tage nach Neumond.<br/>Von links nach rechts:<br/>1. Originalaufnahme<br/>2. Mittelwert<br/>3. Farbe gesättigt<br/>4. aufgehellt]]
[[Datei:North America from low orbiting satellite Suomi NPP.Original.Durchschnitt.gesaettigt.abgedunkelt.png|mini|links|hochkant=4|Sichtbarmachung des Farbwerts mit dem Farbwinkel 201° der Erdoberfläche im Bereich von Nordamerika und dem südöstlichen Pazifik.<br/>Von links nach rechts:<br/>1. Originalaufnahme<br/>2. Mittelwert<br/>3. Farbe gesättigt<br/>4. abgedunkelt]]
<div style="clear:both"></div>
==Einzelnachweise==
<references></references>
<noinclude>
[[Kategorie:Astronomische Kuriositäten]]
</noinclude>
4n2ouaogm74nlzju5g9ckt2vz13vpbu
1092185
1092181
2026-10-17T11:15:57Z
Bautsch
35687
/* Siderischer Monat */ überarbeitet
1092185
wikitext
text/x-wiki
[[Datei:Mondfinsternis.7.9.2025.20.46MESZ.Berlin.png|mini|rechts|hochkant=2|Ein bei Vollmond während einer Mondfinsternis im Kernschatten der Erde befindlicher Blutmond nach Mondaufgang von Berlin aus gesehen am 7. September 2025.]]
== Monat ==
Die Bezeichnung '''Monat''' stammt etymologisch von unserem Erdmond ab. Es handelt sich um ein Erbwort, das auf die seit dem 8. Jahrhundert bezeugten althochdeutschen Formen ''mānōd'' beziehungsweise ''mānōth'' zurückgeht. Diese wiederum stammen vom indoeuropäischen Wort ''mēnōt'' ab, das sowohl ''Monat'' als auch ''Mond'' bedeuten kann.<ref>[https://indogermanisch.org/pokorny-etymologisches-woerterbuch/m%C4%93n%C5%8Dt_gen_m%C4%93neses_woraus_m%C4%93nes-_m%C4%93ns-_m%C4%93s-_m%C4%93n.htm mēnōt], Pokorny - Indogermanisches etymologisches Wörterbuch</ref>
== Die Naturgeschichte von Plinius dem Älteren ==
'''Gaius Plinius Secundus''' ("der Ältere") hat im ersten nachchristlichen Jahrhundert über den '''Mond''' geschrieben. Das neunte Kapitel des zweiten Buches aus dem ersten Band hat den Titel ''Von der Natur des Mondes''. Über den Mond schreibt Plinius Folgendes:<ref name="Denso1764">Plinius: ''Naturgeschichte'', erster Band, zweites Buch, neuntes Kapitel: ''Von der Natur des Mondes'', übersetzt von Johann Daniel Denso (1708–1795), Rostock und Greifswald, Anton Ferdinand Rösens Buchhandlung, 1764, in moderne deutsche Sprache gesetzt.</ref>
<blockquote>
Aber alle diese [Wandelgestirne] übertrifft an Bewunderungswürdigkeit das Gestirn des '''Mondes''', das niedrigste, der Erde bekannteste, und zur Verminderung der Finsternis erfunden. Dieser hat durch seinen Wandel und veränderte Gestalt, da er beständig zu- und abnimmt, den Witz seiner Beobachter gequält, die es sich für einen Schimpf hielten, wenn man das nächste Gestirn nicht kennen sollte. Ja er ist auch in Spitzen gekrümmt, bald gerade halbgeteilt, bald in eine ganze Scheibe gebogen, bald, da er einem dunkeln Flecken ähnlich war, scheint er plötzlich hervor, und ist ist er ohne Grenzen rund und voll, ist gar nicht zu sehen. Bald scheint er die ganze Nacht durch, bald geht er spåt auf, und verstärkt noch bei Tage den Schein der Sonne. Er wird verfinstert, und lässt sich auch in der Verfinsterung noch sehen. Beim Ausgang des Monats sieht man ihn nicht, und man glaubt nicht, dass er alsdenn verfinstert werde.<br/>
Bald steht er niedrig, bald erhaben, und auch dies nicht auf eine Art. Bald steht er in der Höhe des Himmels, bald reicht er an die Berge: jetzt steht er hoch gegen Norden, bald tief gegen Süden herunter. Alles dies hat '''Endymion''' unter allen Menschen an ihm zuerst wahrgenommen, und deswegen ist die Sage, dass dieser in ihm verliebt gewesen sei. Wir sind wahrlich gegen diejenigen, welche uns zuerst bei diesem Himmelslicht durch ihre Arbeit und Sorge ein Licht aufgesteckt haben, nicht dankbar. Es ist ein wunderbares Verderben des menschlichen Witzes, dass wir ein Vergnügen daran finden, Blutvergießen und Mord in unsern Jahrbüchern aufzuzeichnen, damit die, welche die Welt nicht kennen, doch von unsern Lastern wissen mochten.<br/>
Doch aber da der Mond der Weltachse am nächsten steht, und also den engsten Umlauf hat, so durchlauft er in 27 und 1/3 Tag eben den Raum, welchen der höchste Stern des Saturns, wie gesagt ist, in dreißig Jahren zurücklegt. Hiernächst hält er sich in der Zusammenkunft mit der Sonne zwei Tage, wenn es am langsamsten geschieht, aus, und geht mit dem dreißigsten Tag zu seinen vorigen Abwechselungen wieder hervor. Ich weiß nicht, ob er nicht unter allen am Himmel der einzige Lehrmeister gewesen sei, dass man das Jahr in einen Raum von zwölf Monaten einteilen müsse, denn ebenso oft folgt er der Sonne, wenn sie zu einem neuen Zeichen zurückkehrt.<br/>
Er wird, wie die übrigen Sterne vom Sonnenlicht verdeckt. Denn wir sehen, dass er von derselben sein Licht gänzlich entlehne, und damit dergestalt leuchte, als wenn wir ihr zurückprallendes Licht in einem Wasser schweben sehen. Da er die Feuchtigkeit mit sanfterer und unvollkommener Kraft auflöst, auch wohl vermehrt, welche die Strahlen der Sonne verzehren. Hierher kommt es auch, dass man ihn in ungleichem Licht sieht, dass er alsdenn nur voll ist, wenn er der Sonne gerade gegenübersteht, in den übrigen Tagen nur so viel desselben, als er von jener empfängt, auf seiner Scheibe zeigt. Wenn er mit der Sonne zusammenkommt, kann man ihn nicht sehen, weil er uns die dunkle Seite zukehrt, und alles Geschöpfte dorthin, woher er es empfangen hat, wieder zurückwirft.<br/>
Es scheint außer Zweifel gesetzt zu sein, dass die Gestirne ihre Nahrung von der Feuchtigkeit der Erde haben. Denn er wird auf der einen Hälfte seiner Scheibe oft voller Flecken gesehen, weil er etwa noch nicht Kraft genug hat, mehr als ihm dienlich ist, an sich zu ziehen. Seine Flecken sind gewiss nichts anderes, als die mit der Feuchtigkeit von der Erde mit fortgerissenen Unreinigkeiten.<br/>
</blockquote>
<div style="clear:both"></div>
== Mondperioden ==
Die zu beobachtende scheinbare Mondbahn kann im Verlauf verschiedener Perioden durch zahlreiche '''Mondzyklen''' beschrieben werden. Die kürzesten Zyklen dauern ungefähr einen Monat im Sonnenkalender, sie längeren Mondzyklen können aber auch mehrere Jahre umfassen.
Der Mond hat ähnlich wie die Sonne einen scheinbaren Winkeldurchmesser von ungefähr 30 Bogenminuten beziehungsweise 0,5 Bogengrad. Dies entspricht bei Betrachtung des eigenen Fingers mit ausgestrecktem Arm in etwa einem Viertel der Fingerdicke.
===Synodischer Monat===
[[Datei:Courtenay.Mondsichel.P1033938.png|mini|Altlicht beim Morgenletzt des Mondes (heliakischer Aufgang) über dem östlichen Horizont auf Vancouver Island kurz nach der Tag-und-Nachtgleiche im September 2019.]]
[[Datei:Neulicht.37Tauri.P1138734.jpg|mini|Neulicht beim Abenderst des Mondes (akronychischer Untergang) über dem westlichen Horizont in Berlin Anfang Mai 2022.]]
Der synodische Monat ist durch den Verlauf der Elongation des Mondes in Bezug zur Sonne beschrieben.
Der Mond umrundet die Erde ungefähr zwölfmal schneller als die Erde die Sonne und benötigt für einen Umlauf einen Monat. Die einfachste Wahrnehmung des Mondlaufs ergibt sich durch die Beobachtung der Mondphasen beziehungsweise der Elongationen des Mondes. Der '''synodische Monat''' (altgriechisch ''σύνοδος'' (''synodos'') = ''Zusammentreffen'') beschreibt die Dauer zwischen zwei gleichen Mondphasen, also von Neumond zu Neumond beziehungsweise von Vollmond zu Vollmond. Hier wird gemeinhin das Zusammentreffen von Neumond und Sonne am Himmel als Referenzzeitpunkt betrachtet. Ein synodischer Monat dauert etwa 29,53 Tage, und zwölf synodische Monate dauern demzufolge rund 354,37 Tage - das sind gut fünfeinhalb Tage weniger als 360. Dieser Zyklus ist dies Basis für die gängigen Mondkalender (Lunarkalender) mit der gegenüber dem am Sonnenjahr orientierten Solarkalender um zirka 11 Tagen kürzeren Jahreslänge. Bei Lunisolarkalendern wird durchschnittlich alle drei Jahre ein dreizehnter synodischer Monat eingeschaltet, damit der Frühlingspunkt der Sonne ungefähr in der gleichen Jahreszeit bleibt.
→ Zur Zahl Zwölf siehe auch [[Quadriviale Kuriositäten/ Zahlen#Zur Zwölf|Exkurs '''Zur Zwölf''']].
[[Datei:Horusauge.Faltung.png|mini|rechts|hochkant=2|Das altägyptische Horusauge als Folge von Rechtecken mit jeweils der Hälfte der Fläche des Vorgängers in einem Rechteck mit dem Flächeninhalt eins.]]
[[Datei:Dendera_Deckenrelief_03.JPG|mini|hochkant=2|Deckenrelief im altägyptischen Tempel von Dendera mit der Darstellung von 15 Mondphasen von links (Neumond) nach rechts (Vollmond) mit den Göttern Junit, Sopdet-Tjenenet, Hor-Behdeti, Hathor, Nephthys, Harsiese, Isis, Osiris, Nut, Geb, Tefnut, Schu, Atum und Month. Im Vollmond vor dem Gott des Mondes Thot ist das von ihm geheilte linke Auge („Mondauge“) des Lichtgottes Horus dargestellt.]]
Die Ägypter haben ihre Kalender nicht mit einer Siebentagewoche, sondern sowohl mit einer Fünftagewoche als auch mit einer Zehntagewoche geführt. Damit besteht ein synodischer Monat auf weniger als einen halben Tag genau aus sechs Fünftagewochen beziehungsweise aus drei Zehntagewochen. Es wird in der Literatur manchmal darauf hingewiesen, dass das Verhältnis der Länge eines synodischen Monats zu dreißig vollen Tagen
:<math>\frac {29,530589 \text{d}} {30 \text{d}} = 0,984353</math>
fast identisch mit dem folgenden Verhältnis ist (siehe auch Horusauge und Heqat in der altägyptischen Geschichte<ref>Donald Frazer: ''Hieroglyphs and Arithmetic of the Ancient Egyptian Scribes'', Kapitel 2.6.5 ''Hekat Fractions and Ro'', Xlibris Corporation, 2012, ISBN 9781469136462</ref>):
:<math>\frac {1} {2} + \frac {1} {4} + \frac {1} {8} + \frac {1} {16} + \frac {1} {32} + \frac {1} {64}</math>
:<math>= \frac {32} {64} + \frac {16} {64} + \frac {8} {64} + \frac {4} {64} + \frac {2} {64} + \frac {1} {64}</math>
:<math>= \frac {63} {64} = \frac {2^6 - 1} {2^6} = 1 - 2^{-6} = 0,984375</math>
Die Abweichung der beiden Verhältnisse beträgt nur 0,022 Promille. Erst nach rund 44700 Monaten oder 3700 Jahren hat sich diese Abweichung auf einen Tag aufsummiert.
Die verschiedenen Mondphasen waren für die Menschen schon immer sichtbar und konnten im Laufe eines synodischen Monats verfolgt werden. Es wird davon ausgegangen, dass zum Beispiel auch auf der Himmelsscheibe von Nebra mindestens eine Mondsichel dargestellt ist, eventuell auch der Vollmond und nach dem österreichischen Ur- und Frühgeschichtler '''Paul Gleirscher''' zusätzlich das Altlicht des Mondes:<ref>Paul Gleirscher: [https://dialnet.unirioja.es/servlet/articulo?codigo=4142096 Zum Bildprogramm der Himmelsscheibe von Nebra: Schiff oder Sichel?], Germania: Anzeiger der Römisch-Germanischen Kommission des Deutschen Archäologischen Instituts, Band 85, Nummer 1, ISSN 0016-8874, Seiten 23 bis 33, 2007</ref>
<gallery caption="Verschiedene möglicherweise auf der Himmelsscheibe von Nebra dargestellte Mondphasen" perrow="2" widths="300" heights="300">
Vollmond.P1080516.jpg|Ein im Dezember um Mitternacht fast im Zenit stehender, sehr heller Vollmond.
Zunehmende.Mondsichel.png|Ein zunehmender Mond drei Tage nach Neumond beim akronychischen Untergang am westlichen Abendhimmel drei Wochen vor der Tag-und-Nacht-Gleiche im Herbst. Die rötliche Färbung entstand genauso wie bei der untergehenden Sonne durch die Rayleigh-Streuung in der Erdatmosphäre.
Altlicht.3.Nov.2021.P1116624.jpg|Das Altlicht eines abnehmenden Mondes (Morgenletzt, vier Prozent beleuchtet) beim heliakischen Aufgang am südöstlichen Morgenhimmel der nördlichen Hemisphäre während der bürgerlichen Dämmerung einen Monat nach der Tag-und-Nacht-Gleiche im Herbst mit Erdschein.
Nebra_Scheibe_Modell.jpg|Vollständig rekonstruiertes Modell der bronzenen und mit Gold tauschierten Himmelsscheibe von Nebra.
</gallery>
===Siderischer Monat===
Der '''siderische Monat''' ist durch den Verlauf der ekliptikalen Länge des Mondes in Bezug zum Frühlingspunkt der Sonnenbahn einer astronomischen Epoche definiert und beschreibt die Zeitspanne, nach der der Mond bei seinem Umlauf um die Erde Richtung Osten erneut die ekliptikale Länge des Frühlingspunktes erreicht hat. Da sich er Frühlingspunkt gegenüber dem Fixsternhimmel entlang der Ekliptiklinie verschiebt, ist der siderische Monat vom '''tropischen Monat''' zu unterscheiden. Letzterer bezieht sich ebenfalls auf den Frühlingspunkt, allerdings den des aktuellen Datums und nicht den, der zu einem bereits vergangenen und unveränderlichen Referenzzeitpunkt des Datums einer bestimmten astronomischen Epoche gegolten hat. Zur Zeit wird hierfür meist die Julianische Standardepoche J2000.0 verwendet, die sich auf die Uhrzeit 11:58:55,8 UTC am 1. Januar 2000 bezieht. Bezogen auf diese Standardepoche dauert ein tropischer Monat 27,32158 Tage. Durch die langsame gegenläufige Wanderung des Frühlingspunkts Richtung Westen besteht ein kleiner Unterschied zwischen tropischem und siderischem Monat, der mit 27,32166 Tage geringfügig länger ist. Die Differenz von 0,00008 Tagen entspricht einer Abweichung von von lediglich sieben Sekunden pro Monat: Dies bedeutet, dass der Mond den Frühlingspunkt des aktuellen Datums nach einer Umrundung (griechisch ''tropos'' = ''Drehung'') der Erde sieben Sekunden früher erreicht als einen Stern (lateinisch ''sideris'' = ''des Sterns'') der sich am Anfang der Umrundung noch beim Frühlingspunkt des einen Monat alten Datums befunden hat.
Es kann auch die Zeitspanne betrachtet werden, in der der Mond in Bezug auf den Fixsternhimmel entlang der Ekliptik wieder an einer beliebigen gleichen Stelle erscheint. Dies ist also beispielsweise auch die Dauer zwischen zwei aufeinanderfolgenden Erscheinungen des Mondes im '''Goldenen Tor der Ekliptik''', da dessen Lage durch die Sterne des Fixsternhimmels bei einer bestimmten ekliptikalen Länge bestimmt ist.
→ Siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Das_Goldene_Tor_der_Ekliptik|Exkurs '''Das Goldene Tor der Ekliptik''']].
Die Einteilung der 360 Bogengrad langen Ekliptik in 28 gleiche Teile ist in der Bronzezeit verbreitet gewesen. Daraus ergibt sich ein grobes Koordinatenraster für die ekliptikale Länge des Mondes mit 28 Mondhäusern respektive Mondstationen.
Auf der '''Stachelscheibe von Platt''' aus der Bronzezeit (um 1500 vor Christus) werden die 28 Mondorte der Tage eines Monats beispielsweise durch eine Kreisreihe dargestellt. Die Hohlform diente zur Herstellung von Schmuckscheiben und hat insgesamt sieben konzentrische Kreise. Davon bestehen zwei aus 12 (innen) beziehungsweise aus 28 (außen) gleichmäßig verteilten Mulden.<ref>Irene Hager und Stefan Borovits (Wien, Österreich): ''Der Vorläufer einer Oktaëteris auf dem Kalenderstein bei Leodagger/Pulkau?'', Kapitel 26.2.2 ''Astronomisch/kalendarische "Zählmaschinen" aus der Bronzezeit'', in: Gudrun Wolfschmidt (Herausgeberin): ''Orientierung, Navigation und Zeitbestimmung - Wie der Himmel den Lebensraum des Menschen prägt'', Proceedings der Tagung der Gesellschaft für Archäoastronomie in Hamburg 2017, Band 42 von Nuncius Hamburgensis - Beiträge zur Geschichte der Naturwissenschaften, Verlag tredition, 2019, ISBN 9783749767717</ref> Die zwölf inneren Mulden entsprechen entlang der Ekliptik den 12 Sonnenorten (Monaten) in einem tropischen Jahr beziehungsweise den 12 Jupiterorten (Jahren) in zwölf Jahren. Die 28 äußeren Mulden entsprechen entlang der Ekliptik den 28 Mondorten und somit den Tagen in einem siderischen Monat. Der große kreisförmige Stachel im Zentrum der Scheibe könnte als Symbol für die Sonne stehen. Auf ihm konnte die Scheibe von unten zentrisch und drehbar gelagert werden. Mit der Scheibe konnte (abgesehen vom ungefähr alle drei Jahre zusätzlich erforderlichen 14. siderische Schaltmonat) zwölf Jahre lang in täglich wechselnden Kombinationen in den beiden Lochreihen die Lagen von Mond und Jupiter markiert und abgelesen werden. Damit konnte nach einer Einmessung der Ost-West-Richtung zum Beispiel bei der Tag-und-Nacht-Gleiche zur Jupiterrichtung im Frühlingspunkt der gesamte Lebewesenkreis (Zodiak) jederzeit mit dem täglich ein Mondhaus weiterwandernden Mond vollständig bestimmt werden, auch ohne dass der Jupiter immer sichtbar sein musste. Mit dieser Information ist es dann auch ohne weiteres möglich, das am Himmel nicht sichtbare Lebewesenzeichen zu bestimmen, in dessen Richtung sich die Sonne jeweils gerade befindet.
Im der indischen Astronomie wurden zu diesem Zweck spätestens 500 Jahre danach die '''27 Mondhäuser''' (oder Mondstationen) eingeführt. Da sich die siderische und die synodische Periode um gut zwei Tage unterscheiden, liegen aufeinanderfolgende Neumonde oder Vollmonde in verschiedenen Mondhäusern, nach denen im hinduistischen Lunisolarkalender die Monate benannt werden.
Dieses System wurde etwas später von den Arabern mit '''28 Mondhäusern''' modifiziert. Das '''erste Mondhaus''' liegt bei beiden Einteilungen im Frühlingspunkt in der Epoche um Christi Geburt im '''Kopf des Lammes''' beziehungsweise des Widders (Aries) bei den nördlich der Ekliptik liegenden Sternen Scheratan und Hamal (indisch ''Ashvini'' = ''die beiden Rosseschirrenden'' und arabisch ''aš-šaraṭān'' = ''Die beiden Zeichen''). Für das '''zweite Mondhaus''' folgt der '''Bauch des Lammes''' (indisch ''Bharani'' = ''der Wegtragende'' und arabisch ''al-buṭayn'' = ''das Bäuchlein''). Die Plejaden (indisch ''Krittika'' und arabisch ''aṯ-ṯurayyā'') im fetten '''Schwanz des Lammes''' markieren im Anschluss das '''dritte Mondhaus'''. Das '''vierte Mondhaus''' ist durch den roten Riesenstern Aldebaran (arabisch ''al-dabarān'' = ''der Nachfolgende'', indisch ''Rohini'' = ''der Rötliche'') im Sternbild Stier (Taurus) gekennzeichnet.
<gallery caption="Mondstationen" mode="packed" widths="600" heights="600">
Mondhaeuser.Ekliptik.zirkular.png|Die in eine ringförmige Darstellung projizierten 28 Mondhäuser (von 1 bis 28 entgegen dem Uhrzeigersinn) mit den wichtigsten Sternen entlang der Ekliptik (rote gestrichelte Linie '''zur Epoche J0000.0'''). Der Beobachter befindet sich auf der Erde im Zentrum der Darstellung. Nach innen werden die südlichen und nach außen die nördlichen ekliptikalen Breiten gemessen. Die scheinbare Mondbahn pendelt zwischen den beiden zur Ekliptikline benachbarten Hilfslinien. Der Abstand der Hilfslinien beträgt senkrecht zur Ekliptiklinie immer fünf Bogengrad und entlang der Ekliptiklinie immer knapp dreizehn Bogengrad. Mit bis zum Nordpol zunehmender geographischer Breite des Beobachtungspunktes können auch noch knapp ein Bogengrad südlichere ekliptikale Breiten von der Mondscheibe erreicht werden, am Südpol auch noch entsprechend nördlichere ekliptikale Breiten.
Stachelscheibe_Model_zweiseitig.jpg|Die in Niederösterreich gefundene und aus Sandstein gefertigte Gussform für die '''Stachelscheibe von Platt'''.<br/>Von innen nach außen gibt es '''sieben''' konzentrische Kreise, die folgendermaßen zugeordnet werden können:<br/>- Eine große zentrische Bohrung (im Gussteil eine große stachelartige Erhebung für die '''Sonne''').<br/>- Zwölf gleichmäßig verteilte kleine Bohrungen (Zodiak mit '''zwölf''' Sternzeichen sowie für die Umlaufzeit des Planeten '''Jupiter '''in Jahren).<br/>- Drei äquidistante Kreislinien (die drei inneren Planeten '''Merkur''', '''Venus''' und '''Mars''').<br/>- Achtundzwanzig gleichmäßig verteilte kleine Bohrungen ('''Mond'''häuser).<br/>- Ein großer abschließender Kreis ('''Saturn''' als der Langsame und Beständige).
Stonehenge_phase_one.jpg|Die älteste belegte kreisförmige Struktur in Stonehenge 1 (zirka 3100 bis 2900 vor Christi) besteht aus den 56 Aubrey-Löchern (in der Abbildung weiße Kreise). Diese können unter Verwendung von Quadranten, die geographisch durch die vier Himmelsrichtungen in jeweils 14 Mondstationen geteilt sind, dazu verwendet worden sein, die ekliptikale Länge des Mondes in Bezug zum Frühlingspunkt oder zum Herbstpunkt beziehungsweise in Bezug zur Jupiterposition täglich zu markieren (der Mond erreicht den Jupiter ungefähr alle 27,5 Tage). In dieser Zählung wären alle 28 Mondhäuser halbiert, in eine Tagesstation und eine Nachtstation.
</gallery>
Zwischen dem dritten und vierten Mondhaus liegt das Goldene Tor der Ekliptik, wo der Frühlingspunkt zu Beginn der maltesischen Tarxien-Phase lag. Man beachte die fehlenden helleren ekliptiknahen Sterne im '''Trichter der Thuraya''' westlich davon, also rechts der Plejaden (ekliptikale Länge ungefähr 32 Bogengrad) bis hin zum Stern Hydor heutigen Sternbild Wassermann (Aquarius, ekliptikale Länge ungefähr 314 Bogengrad). Die hellsten ekliptiknahen Sterne in diesem Gebiet des Sternenhimmels Alpherg im Sternbild Fische (Pisces) sowie Hydor und Ancha im Sternbild Wassermann (Aquarius) erreichen lediglich die vierte Größenklasse (4<sup>m</sup>), so dass zwischen dem auffälligen offenen Sternhaufen der Plejaden und Deneb Algedi, dem hellsten Stern im Sternbild Steinbock (Capricornus), auf einer Länge von 90 Bogengrad keine hellen ekliptiknahen Sterne vorhanden sind.
→ Zur Einteilung der Ekliptik nach den monatlichen Mondstationen siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Konjunktionen#Mondhäuser|Exkurs '''Mondhäuser''']]
→ Zum dunklen Himmelsquadranten entlang der Ekliptik siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Der_Trichter_der_Thuraya|Exkurs '''Der Trichter der Thuraya''']]
===Drakonitischer Monat===
[[Datei:Drakonitischer.Monat.png|rechts|mini|hochkant=2|Schematische Darstellung der Mondbahn (gelb) im Laufe eines drakonitischen Monats in Bezug auf die Ekliptiklinie (rot). Nach dem Erreichen der südlichsten Lage in Bezug zur Ekliptiklinie wird die Mondbahn aufsteigend, und von der nördlichsten Lage in Bezug auf die Ekliptiklinie wird die Mondbahn dann wieder absteigend. In der deutschsprachigen Schweiz gibt es für diese im Laufe eines drakonitischen Monats täglich mehr oder weniger deutlich wahrnehmbaren Änderungen der ekliptikalen Breite sogar eigene Adjektive. Das Ansteigen der ekliptikalen Breite des Mondes nach Norden wird '''obsigend''' und das Abfallen des Mondes nach Süden '''nidsigend''' genannt. Direkt auf der Ekliptik befinden sich der aufsteigende und der absteigende Knoten der Mondbahn.]]
Der drakonitische Monat ist durch den Verlauf der ekliptikalen Breite des Mondes in Bezug zur Ekliptiklinie beschrieben.
Deswegen gibt es noch den '''drakonitischen Monat''' (altgriechisch ''δράκων'' (''drakon'') beziehungsweise lateinisch ''draco'' = ''Drache''), der eine Dauer von 27,212 Tagen hat. Diese Dauer beschreibt die Zeitpunkte, an denen die um gut 5 Bogengrad zur Ekliptik geneigte Mondbahn die Ekliptik kreuzt; die ekliptikale Breite des Mondes ist dann exakt null. Diese Schnittpunkte werden Mondknoten genannt und werden einmal im Monat im aufsteigenden Mondknoten und einmal im absteigenden Mondknoten erreicht. Befindet sich der Mond auf der Ekliptik, also in der Nähe dieser Mondknoten, kommt es bei dessen Sonnennähe (wenn der Neumond also in Konjunktion mit der Sonne steht) zu einer Sonnenfinsternis und bei dessen Sonnenferne (wenn der Vollmond also in Opposition zur Sonne steht) zu einer Mondfinsternis. Diese Mondpunkte wurden früher als Drachenpunkte bezeichnet, was sich aus der Vorstellung ableitete, dass ein Drache bei einer Mondfinsternis den Mond beziehungsweise bei einer Sonnenfinsternis die Sonne verschlingen würde.
Mit dem folgenden Java-Programm können die ekliptikalen Koordinaten der Sonne und des Mondes für jeden beliebigen Zeitpunkt eines Julianischen Datums in Julianischen Jahrhunderten in Bezug auf die astronomische Standardepoche J2000 berechnet werden:
'''[[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/ EkliptikaleKoordinatenMondSonne|→ Java-Programm "EkliptikaleKoordinatenMondSonne"]]'''<ref>Unter Verwendung der Formeln aus: Oliver Montenbruck, Thomas Pfleger: ''Astronomie mit dem Personal Computer'', Springer-Verlag Berlin Heidelberg GmbH, 1989, ISBN 978-3-662-05865-7</ref>
[[Datei:Ekliptikale.Breiten.Mond.siderischer.Monat.png|links|mini|hochkant=4|Die ekliptikalen Breiten des Mondes im Verlauf eines drakonitischen Monats beziehungsweise eines nur gut zweieinhalb Stunden längeren siderischen Monats mit gut 27 Tagen.]]
{| class="wikitable"
|+ Die täglichen Änderungen der ekliptikalen Breite des Mondes in Bogengrad innerhalb eines Mondviertels
!title="Tage nach aufsteigendem Knoten"| Tage nach<br/>aufsteigendem<br/>Knoten
!title="Änderung der ekliptikalen Breite"| Änderung der<br/>ekliptikalen Breite<br/>zum Vortag
|-
| 1 || 1,2°
|-
| 2 || 1,1°
|-
| 3 || 1,0°
|-
| 4 || 0,9°
|-
| 5 || 0,6°
|-
| 6 || 0,3°
|-
| 7 || 0,0°
|}
<div style="clear:both"></div>
Der Mond kann auf seiner Bahn im Laufe der Zeiten alle ekliptiknahen Himmelsobjekte inklusive aller Planeten und der Sonne bedecken und innerhalb einer Stunde wieder freigeben, die sich in einem Band bis zu gut ±5 Bogengrad nördlich oder südlich neben der Ekliptiklinie befinden.
'''{{w|Aristoteles}}''' (384 bis 322) hat dies in seiner Schrift '''"Über den Himmel"''' (altgriechisch: ''Περὶ οὐρανοῦ'' / ''Peri uranu'') anhand der von ihm beobachteten '''Bedeckung des Planeten Mars durch den zunehmenden Halbmond in der Nähe des Sterns Regulus''' (α Leonis) beschrieben und darauf hingewiesen, dass die Babylonier und die Ägypter solche Phänomene über lange Zeit beobachtet und dokumentiert hatten.<ref>Aristoteles: [http://classics.mit.edu/Aristotle/heavens.2.ii.html On the Heavens], Teil 12, Buch II, um 350 vor Christus, ins Englische übersetzt von John Leofric Stocks (* 1882; † 1937)</ref>
→ Siehe [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Konjunktionen#Aristotelische Mars-Konjunktion|Abschnitt '''Aristotelische Mars-Konjunktion''']].
Der ekliptiknahe Hauptstern Pollux im Sternbild Zwillinge (Gemini) hat sich aufgrund seiner Eigenbewegung im Laufe der letzten zehntausend Jahre so weit von der Ekliptiklinie entfernt, dass er inzwischen nicht mehr vom Mond bedeckt werden kann.
→ Für helle ekliptiknahe Objekte siehe [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Der_Fixsternhimmel#Die_sieben_hellsten_Objekte_der_Ekliptik|Abschnitt '''Die sieben hellsten Objekte der Ekliptik''']].
===Siderische Mondperioden===
Der Mond erscheint innerhalb eines tropischen Jahres dreizehn- oder vierzehnmal an einer bestimmten Stelle des Fixsternhimmels, wobei er wegen der unterschiedlichen Periodendauern von siderischen und synodischen Monaten immer ein anderes '''Mondalter''' (die Anzahl der Tage seit dem letzten Neumond) und wegen der unterschiedlichen Periodendauern von siderischen und drakonitischen Monaten immer eine andere ekliptikale Breite aufweist.
Die beiden folgenden Diagramme sollen den zeitlichen Verlauf der Mondphasen und der ekliptikalen Breiten des Mondes bei seinem Erscheinen im Goldenen Tor der Ekliptik während 254 aufeinanderfolgender siderischer Perioden mit jeweils 27,322 Tagen (insgesamt 6940 Tage beziehungsweise 19 Jahre) veranschaulichen:
[[Datei:Mondphasen.siderische.Perioden.png|links|mini|hochkant=4|Die Mondphasen bei aufeinanderfolgenden Erscheinungen des Mondes im Goldenen Tor der Ekliptik innerhalb von 254 siderischen Perioden (insgesamt 19 Jahre). Da der synodische Monat (von Neumond zu Neumond) über zwei Tage länger ist als der siderische Monat, kommt es hierbei zu Verschiebungen, weil der Mond nach Ablauf eines siderischen Monats beim Erreichen derselben ekliptikalen Länge noch nicht ganz wieder sein maximales Mondalter erreicht hat.<br/><br/>In der oberen Hälfte des Diagramms sind zunehmende und in der unteren Hälfte abnehmende Monde zu beobachten. Eine Mondphase von 0 Prozent steht für einen Neumond und eine Mondphase von ±100 Prozent für einen Vollmond.<br/><br/>Der Startpunkt (Tag 0 im Monat 0) kann zum Beispiel mit dem 22. Mai 2020 am Abend (UTC) angesetzt werden, an dem der Neumond zusammen mit der Sonne im Goldenen Tor der Ekliptik stand. Dies geschieht dann nach 19 Jahren am 23. Mai 2039 kurz nach Mitternacht (UTC) erneut.]]
[[Datei:Ekliptikale.Breiten.Mond.siderische.Perioden.png|links|mini|hochkant=4|Die ekliptikalen Breiten des Mondes bei aufeinanderfolgenden Erscheinungen des Mondes im Goldenen Tor der Ekliptik innerhalb von 254 aufeinanderfolgenden siderischen Perioden (insgesamt 19 Jahre). Da der drakonitische Monat (von einem aufsteigendem Mondknoten bis zum nächsten aufsteigenden Mondknoten) gut zweieinhalb Stunden kürzer ist als der siderische Monat, kommt es hierbei zu Verschiebungen, weil der Mond nach Ablauf eines siderischen Monats den aufsteigenden Knoten bereits wieder hinter sich gelassen hat.<br/><br/>Bei großen ekliptikalen Breiten (oben) kommt es im Goldenen Tor der Ekliptik zu Bedeckungen der Plejaden und bei kleinen ekliptikalen Breiten (unten) kommt es zu Bedeckungen der Hyaden oder des Sterns Aldebaran durch die Mondscheibe.<br/><br/>Der Startpunkt (Tag 0 im Monat 0) kann beispielsweise ebenfalls mit dem 22. Mai 2020 angesetzt werden, an dem der Neumond vom Erdmittelpunkt aus gesehen bei einer ekliptikalen Breite von zirka -2,5 Bogengrad unterhalb der Sonne, deren ekliptikale Breite definitionsgemäß 0 Bogengrad beträgt, im Goldenen Tor der Ekliptik stand.<br/><br/>Nach 18,61 Jahren (beziehungsweise 6793,5 Tagen oder gut 230 synodischen Monaten, in dieser Abbildung also nach gut 248,6 siderischen Monaten) erreicht der Mond dieselbe ekliptikale Breite und fast die gleiche Mondphase, befindet sich dann allerdings bei einer anderen ekliptikalen Länge.<br/><br/>Die kurzperiodische kleine Wellenbewegung kommt durch die Nutation der Erdachse im Bezug zur Ekliptik beziehungsweise zum Fixsternhimmel zustande; sie hat eine Periodendauer von 35 Tagen und überlagert sich mit den zirka eine Woche kürzeren siderischen Mondperiode.]]
<div style="clear:both"></div>
===Der Meton-Zyklus===
Nicht nur die Bestimmung und Vorhersage der Auf- und Untergänge der Venus haben die Aufmerksamkeit der Astronomen des Altertums auf sich gezogen, sondern auch der Mondzyklus mit den verschiedenen Mondphasen sowie das Auftreten von Mondfinsternissen bei Vollmond und von Sonnenfinsternissen bei Neumond. Es gibt einen Zyklus, der die Zeit beschreibt, nachdem die Sonne und der Mond die gleiche Konstellation erreichen. Nach '''19 Jahren''' (beziehungsweise knapp '''6940 Tagen''') hat nicht nur die Sonne dieselbe ekliptikale Länge erreicht, sondern auch der der Mond (nach '''254 siderischen Monaten'''), und er hat daher auch dieselbe Mondphase (nach '''235 synodischen Monaten'''). Außerdem hat er dann auch noch annährend die gleiche ekliptikale Breite (nach '''255 drakonitischen Monaten'''), so dass er fast wieder an derselben Stelle des Fixsternhimmels steht.<ref name="rutherforth">Thomas Rutherforth: "A System Of Natural Philosophy: Being A Course of Lectures In Mechanics, Optics, Hydrostatics, and Astronomy; Which are Read in St Johns College Cambridge", volume 2, chapter XIV: "Of the devision<!--sic!--> of time", paragraph 388: "The cycle of Metos", 990 ff.</ref>
Der Zyklus beruht also im Wesentlichen auf der zwar nur langfristig, bei entsprechender Ausdauer jedoch einfach zu beobachtenden Tatsache, dass 19 tropische Sonnenjahre, 235 synodische Monate, 254 siderische Monate und 255 drakonitische Monate fast die gleiche Länge haben. Der Unterschied zwischen den ersten beiden beträgt nur rund zwei Stunden:
* 19 Jahre = 6939,6 Tage, bestimmt durch die ekliptikale Länge der Sonne mit ihren Äquinoktien und Sonnenwenden.
* 235 synodische Monate = 6939,7 Tage, bestimmt durch die Mondphasen mit Neumonden und Vollmonden.
* 254 siderische Monate = 6939,7 Tage, bestimmt durch die ekliptikale Länge des Mondes.
* 255 drakonitische Monate = 6939,1 Tage, bestimmt durch die auf- und absteigenden Knoten der Mondbahn.
[[Datei:Legende.Meton-Zyklus.png|mini|rechts|Legende zum Diagramm "Meton-Zyklus".]]
<gallery caption="Der Meton-Zyklus" mode="packed">
Meton-Zyklus.Maerz.2019.png|Der Meton-Zyklus anhand der Frühlingsvollmonde (weiße Kreise) am 21. März 2019 (laufende Nummer 0) und am 21. März 2038 (laufende Nummer 235).
Meton-Zyklus.Januar.2023.Januar.2042.png|Der Meton-Zyklus vom Vollmond am 6. Januar 2023 bis zum Vollmond am 6. Januar 2042. Die farbigen Kreise symbolisieren die Äquinoktien und Sonnenwenden.
</gallery>
[[Datei:Meton-Zyklus.2023.13996.png|mini|links|hochkant=4|Der 19-jährige Meton-Zyklus vom Januar 2023 bis zum Januar 2042. Die Zeit schreitet in horizontaler Richtung jeweils um einen halben Tag pro Bildpunkt voran. Der Vollmond (weißer Kreis) erscheint sowohl am 6. Januar 2023 als auch am 6. Januar 2042 im Sternbild Zwillinge (Gemini) nördlich vom Stern Mebsuta (ε Geminorum). Unten sind die kleinen und großen Mondwenden für die geographische Breite 52,5 Bogengrad (zum Beispiel in Berlin) im Laufe der Zeit dargestellt.]]
<div style="clear:both"></div>
Dieser 19-jährige nach dem antiken griechischen Astronomen {{w|Meton}} (5. Jahrhundert vor Christus) benannte '''Meton-Zyklus''' sowie auch der unten erwähnte Saros-Zyklus waren im Altertum spätestens schon den Babyloniern bekannt und dienten als Grundlage für ihren Mondkalender. Meton ist davon ausgegangen, dass 19 Jahre exakt mit 6940 Tagen sowie mit 235 synodischen Monaten übereinstimmen. Dadurch, dass das Jahr nach dieser Annahme genau fünf Neunzehntel Tage länger ist als 365 Tage, sind neunzehn Jahre nach dieser Berechnung genau fünf Tage länger ist als neunzehn Mal 365 Tage, also 6935 Tage. Aus der Annahme einer festen ganzrationalen Kopplung der Umlaufzeiten der Erde um Ihre Achse (Tag) und um die Sonne (Jahr) sowie der Umlaufzeit des Mondes um die Erde (Monat) ergeben sich die folgenden Zusammenhänge:
* Abgerundet auf ganze Zahlen:
** '''Die Jahreslänge in ganzen Tagen:'''
**:<math>\frac {6940} {19} = 365 \text { Rest } 5</math>, das heißt, dass für 19 Jahre mit der Länge 365 Tage '''fünf Schalttage''' (Jahreslänge dann 366 Tage) erforderlich sind, damit der Frühlingspunkt mit dem tropischen Sonnenjahr synchron bleibt (Solarkalender).
** '''Die Monatslänge in ganzen Tagen:'''
**:<math>\frac {6940} {235} = 29 \text { Rest } 125</math>, das heißt, dass für 235 synodische Monate mit der Länge 29 Tage '''125 Schalttage''' (Monatslänge dann 30 Tage) erforderlich sind, damit ein tropisches Sonnenjahr immer zwölf Monate umfasst (Solarkalender).
** '''Die Jahreslänge in ganzen Monaten:'''
**:<math>\frac {\frac {6940} {19}} {\frac {6940} {235}} = \frac {235} {19} = 12 \text { Rest } 7</math>, das heißt, dass in 19 Jahren mit 235 synodischen Monaten sowie 6490 Tagen '''sieben synodische Schaltmonate''' (Jahreslänge dann 13 Monate) erforderlich sind, um das Kalenderjahr mit dem tropischen Sonnenjahr synchron zu halten (Lunisolarkalender).
* Exakt mit Brüchen (ganzrationale Zahlen):
** Die Jahreslänge <math>d_a</math> in Tagen (in einem Sonnenjahr):
**:<math>d_a = \frac {6940 \text { d}} {19 \text { a}} = 365 \frac {\text {d}} {\text {a}} + \frac {5} {19} \frac {\text {d}} {\text {a}} = 365 \frac {\text {d}} {\text {a}} + \frac {20} {76} \frac {\text {d}} {\text {a}} = 365 \frac {\text {d}} {\text {a}} + \frac {1} {4} \frac {\text {d}} {\text {a}} + \frac {1} {76} \frac {\text {d}} {\text {a}} = 365,25 \frac {\text {d}} {\text {a}} + \frac {1} {76} \frac {\text {d}} {\text {a}} \approx 365,263158 \frac {\text {d}} {\text {a}}</math>
** Die Länge eines synodischen Monats <math>d_m</math> in Tagen:
**:<math>d_m = \frac {6940 \text { d}} {235 \text { m}} = \frac {1388} {47} \frac {\text {d}} {\text {m}} = 29 \frac {\text {d}} {\text {m}} + \frac {125} {235} \frac {\text {h}} {\text {m}} = 29 \frac {\text {d}} {\text {m}} + \frac {25} {47} \frac {\text {h}} {\text {m}} \approx 29,531915 \frac {\text {d}} {\text {m}}</math>
** Länge von zwölf synodischen Monaten <math>d_{m_{12}}</math> in Tagen (in einem Mondjahr):
**:<math>d_{m_{12}} = 12 \frac {\text {m}} {\text {a}} \cdot d_m = 12 \frac {\text {m}} {\text {a}} \cdot \frac {1388} {47} \frac {\text {d}} {\text {m}} = \frac {16656} {47} \frac {\text {d}} {\text {a}} \approx 354,382979 \frac {\text {d}} {\text {a}} \approx d_a - 11 \frac {\text {d}} {\text {a}}</math>
** Die Jahreslänge <math>m_a</math> in synodischen Monaten (in einem Sonnenjahr):
**:<math>m_a = \frac {d_a} {d_m} = \frac {\frac {6940 \text { d}} {19 \text { a}}} {\frac {6940 \text { d}} {235 \text { m}}} = \frac {235} {19} \frac {\text {m}} {\text {a}} = 12 \frac {\text {m}} {\text {a}} + \frac {7} {19} \frac {\text {m}} {\text {a}} \approx 12,368421 \frac {\text {m}} {\text {a}}</math>
:'''Anmerkung''': Man nehme zur Kenntnis, dass das der Mittelwert der Dauern vom Sonnenjahr <math>d_a</math> und vom Mondjahr <math>d_{m_{12}}</math> fast genau 360 Tage pro Jahr beträgt, also so viele Tage wie für einen vollständigen Kreis in Bogengrad gerechnet wird:
::<math>\frac {d_a + d_{m_{12}}} {2} = \frac {\frac {6940 \text { d}} {19 \text { a}} + \frac {16656} {47} \frac {\text {d}} {\text {a}}} {2} = \frac {321322} {893} \frac {\text {d}} {\text {a}} \approx 359,823 \frac {\text {d}} {\text {a}} \approx 360 \frac {\text {d}} {\text {a}}</math>
::Mit den heutigen, jeweils rund eine halbe Stunde kürzeren Messwerten für die beiden Jahresdauern (tropisches Sonnenjahr mit 365,241 Tagen und Mondjahr mit zwölf Lunationen und 354,367 Tagen) zur Epoche J2000.0 ergibt sich ein nur geringfügig anderer Mittelwert, der ebenfalls nur um zirka eine Dreiviertelstunde von der Dauer von 360 Tagen abweicht:
::<math>\frac {d_a + d_{m_{12}}} {2} = \frac {365,241 \frac {\text {d}} {\text {a}} + 354,367 \frac {\text {d}} {\text {a}}} {2} \approx 359,804 \frac {\text {d}} {\text {a}} \approx 360 \frac {\text {d}} {\text {a}}</math>
Für diese Erkenntnisse ist entweder die Weitergabe von beobachteten astronomischen Ereignissen, wie der Bedeckung der Plejaden durch den Mond oder die Messung der ekliptikalen Koordinaten des Mondes, an die nächste Generation erforderlich oder ein Lebensalter, das die Beobachtung von mindestens zwei solcher Zyklen umfasst – je nach Zeitpunkt der Geburt also rund 25 bis über 40 Jahre.
Da der Meton-Zyklus mit den aufgerundeten ganzzahligen 6940 Tagen einen Vierteltag länger dauert als 19 Sonnenjahre, summiert sich diese Differenz nach vier Meton-Zyklen auf einen ganzen Tag. Der entsprechende nach {{w|Kallippos von Kyzikos}} (viertes vorchristliches Jahrhundert) benannte '''Kallippische Zyklus''' von 76 Jahren (<math>= 4 \cdot 19</math> Jahre) oder 27759 Tage (<math>= (4 \cdot 6940) - 1</math> Tage) wird auch als verbesserter Meton-Zyklus bezeichnet:
* 76 Jahre = 27758,4 Tage
* 940 synodische Monate = 27758,8 Tage
* 1016 siderische Monate = 27758,8 Tage
* 1020 drakonitische Monate = 27756,5 Tage
Nach ungefähr 48 Sonnenjahren betrug die Differenz zwischen Meton-Zyklus und Sonnenjahr einen Tag, aber erst nach ungefähr 128 Sonnenjahren erreicht die Differenz zwischen Kallippischen Zyklus und Sonnenjahr so groß. Da der Kallippische Zyklus mit genau 27759;Tagen einen Vierteltag länger dauert als 76 Sonnenjahre, summiert sich diese Differenz nach vier Kallippischen Zyklen auf einen ganzen Tag. Der entsprechende nach {{w|Hipparchos (Astronom)|Hipparchos von Nicäa}} (zweites vorchristliches Jahrhundert) benannte '''Hipparchos-Zyklus''' von 304 Jahren (<math>= 4 \cdot 76 = 16 \cdot 19</math> Jahre) = 111035 Tage (<math>= (4 \cdot 27759) - 1</math> Tage) ist also wiederum ein verbesserter Kallippischer Zyklus:
* 304 Jahre = 111033,6 Tage
* 3760 synodische Monate = 111035,0 Tage
* 4064 siderische Monate = 111035,2 Tage
* 4080 drakonitische Monate = 111025,9 Tage
Nach einer Kallippischen Periode beträgt die Differenz zwischen 111035 ganzen Tagen und der Dauer von 3760 synodischen Monaten nur eine dreiviertel Stunde.
Die folgende Tabelle gibt einen Überblick über die Dauern im Meton-Zyklus, im Kallippischen Zyklus und im Hipparchos-Zyklus:
{| class="wikitable"
!
!Tropische Perioden
!Synodische Perioden
!Siderische Perioden
!Ganzzahlige Tage
|-
|'''Dauer einer Periode in Tagen'''
|'''365,2422'''
|'''29,53059'''
|'''27,32166'''
|'''1'''
|-
|
|-
|'''Anzahl der Perioden im Meton-Zyklus'''
|'''19'''
|'''235'''
|'''254'''
|'''6940'''
|-
|'''Anzahl der Tage im Meton-Zyklus'''
|6939,6018
|6939,6887
|6939,7016
|6940
|-
|'''Differenz zu tropischen Perioden in Tagen'''
|0,0000
|0,0868
|0,0998
|0,3982
|-
|'''Differenz zu synodischen Perioden in Tagen'''
| -0,0868
|0,0000
|0,0130
|0,3113
|-
|'''Differenz zu siderischen Perioden in Tagen'''
| -0,0998
| -0,0130
|0,0000
|0,2984
|-
|'''Differenz zu tropischen Perioden in Stunden'''
|0,00
|2,08
|2,40
|9,56
|-
|'''Differenz zu synodischen Perioden in Stunden'''
| -2,08
|0,00
|0,31
|7,47
|-
|'''Differenz zu siderischen Perioden in Stunden'''
| -2,40
| -0,31
|0,00
|7,16
|-
|
|-
|'''Anzahl der Perioden im Kallippischen Zyklus'''
|'''76'''
|'''940'''
|'''1016'''
|'''27759'''
|-
|'''Anzahl der Tage im Kallippischen Zyklus'''
|27758,4072
|27758,7546
|27758,8066
|27759
|-
|'''Differenz zu tropischen Perioden in Tagen'''
|0,0000
|0,3474
|0,3994
|0,5928
|-
|'''Differenz zu synodischen Perioden in Tagen'''
| -0,3474
|0,0000
|0,0520
|0,2454
|-
|'''Differenz zu siderischen Perioden in Tagen'''
| -0,3994
| -0,0520
|0,0000
|0,1934
|-
|'''Differenz zu tropischen Perioden in Stunden'''
|0,00
|8,34
|9,58
|14,23
|-
|'''Differenz zu synodischen Perioden in Stunden'''
| -8,34
|0,00
|1,25
|5,89
|-
|'''Differenz zu siderischen Perioden in Stunden'''
| -9,58
| -1,25
|0,00
|4,64
|-
|
|-
|'''Anzahl der Perioden im Hipparchos-Zyklus'''
|'''304'''
|'''3760'''
|'''4064'''
|'''111035'''
|-
|'''Anzahl der Tage im Hipparchos-Zyklus'''
|111033,6288
|111035,0184
|111035,2262
|111035
|-
|'''Differenz zu tropischen Perioden in Tagen'''
|0,0000
|1,3896
|1,5974
|1,3712
|-
|'''Differenz zu synodischen Perioden in Tagen'''
| -1,3896
|0,0000
|0,2078
| -0,0184
|-
|'''Differenz zu siderischen Perioden in Tagen'''
| -1,5974
| -0,2078
|0,0000
| -0,2262
|-
|'''Differenz zu tropischen Perioden in Stunden'''
|0,00
|33,35
|38,34
|32,91
|-
|'''Differenz zu synodischen Perioden in Stunden'''
| -33,35
|0,00
|4,99
| -0,44
|-
|'''Differenz zu siderischen Perioden in Stunden'''
| -38,34
| -4,99
|0,00
| -5,43
|}
Vor gut 2000 Jahren betrug die Differenz zwischen Kallippischem Zyklus und Sonnenjahr nach ungefähr 227 Sonnenjahren einen Tag. Durch die inzwischen etwas verkürzte Dauer eines tropischen Jahres ist dies heute bereits nach etwa 221 Jahren der Fall.
====Die Goldene Zahl====
Die '''Goldene Zahl''' gibt an, das wievielte von diesen 19 Jahren ein bestimmtes Jahr ist, und sie spielt auch heute noch eine wichtige Rolle bei der Bestimmung des Datums von Pessach oder Ostern, zum Beispiel mit Hilfe der Formeln zur Berechnung von {{w|Carl Friedrich Gauß}} (* 1777; † 1855):
→ Siehe auch:
* [[Astronomie von der Frühgeschichte bis zur Neuzeit/ Kalenderführung/ Osterdatum|'''Osterdatum''']]
* {{w|Gaußsche Pessach-Formel}}
* {{w|Gaußsche Osterformel}}
Der Name Goldene Zahl rührt möglicherweise davon her, dass der diesem Zyklus zugrundeliegende Kalender ('''Parapegma''') des Meton auf den Steinmauern seiner Sonnenuhr (heliotropion) am Pnyx-Hügel in Athen in goldener Schrift zu sehen war.<ref>Michael Wright: [https://www3.astronomicalheritage.net/index.php/show-entity?identity=26&idsubentity=1 The Pnyx, Athens, Greece], Portal to the Heritage of Astronomy, August 2011</ref><ref name="rutherforth" />
Heute ist in den Monaten um die Wintersonnenwende alle 19 Jahre morgens am westlichen Horizont der untergehende Vollmond im Goldenen Tor der Ekliptik zu sehen, wie im Dezember 2018. Die untere Hälfte des Mondes wird dann während des Untergangs vom Horizont verdeckt und der sichtbare leuchtende Teil bildet somit einen Halbkreis, wie er im mittleren Segment der Himmelstafel angedeutet ist. In diesem Fall liegen Hyaden und Plejaden im Westen auf einer Linie parallel zum Horizont und der dazwischenliegende, beim Untergang noch halb zu sehende Vollmond würde der Abbildung auf der Steintafel von Tal-Qadi entsprechen. Vor 4500 Jahren ergab sich diese Himmelsansicht wegen der Verschiebung des Frühlingspunktes bereits um die Tag-und-Nacht-Gleiche im Herbst.
Die 19 Megalithe des Blaustein-Hufeisens von '''Stonehenge''' (2270 bis 1930 vor Christus) werden ebenfalls mit dem 19-jährigen Meton-Zyklus in Zusammenhang gesehen.
Im Übrigen werden auch die Goldhüte aus der Bronzezeit mit diesem Zyklus in Verbindung gebracht. Der '''Berliner Goldhut''' zeigt in der siebenten und in der neunten ringförmigen Zone von oben horizontale Reihen mit 57 (= 3 x 19) kreisförmigen Punzen, die für ein Viertel der 228 Monate einer Meton-Periode stehen könnten.<ref>Wilfried Menghin: „Der Berliner Goldhut und die goldenen Kalendarien der alteuropäischen Bronzezeit“, Acta Praehistorica et Archaeologica, Band 32, 2000, ISSN 0341-1184, Seiten 31 bis 108</ref>
An der östlichen Wand in der Kamme A des hethitischen Heiligtums '''Yazılıkaya''' (türkisch für „beschriebener Fels“) aus dem 13. vorchristlichen Jahrhundert existiert eine Reliefbilderreihe, die eine Prozession von '''neunzehn''' nach links schauenden Göttinnen im Ganzkörperprofil darstellt. Auch hier wird vermutet, dass diese Reihe als Zählwerk für den Meton-Zyklus eine Kalenderfunktion innehatte.<ref>Eberhard Zangger, Rita Gautschy: [http://63.33.38.154/JSA/article/view/12232 Celestial Aspects of Hittite Religion - An Investigation of the Rock Sanctuary Yazilikaya], Journal of Skyscape Archaeology, 5(1), 5–38, 2019</ref><ref>Edwin C. Krupp, Eberhard Zangger: [https://www.archaeologie-online.de/artikel/2021/die-symbolische-darstellung-des-kosmos-im-hethitischen-felsheiligtum-yazlkaya Die symbolische Darstellung des Kosmos im hethitischen Felsheiligtum Yazılıkaya] vom 16. Juni 2021, Archäologie Online, archaeomedia, Freiburg</ref>
Der Zyklus mit der Anzahl 19 taucht in weiteren archäoastronomischen Kontexten auf:
* Mond-Sonne-Darstellung aus dem mesoamerikanischen '''Codex Borbonicus''' der Azteken vom Anfang des 16. Jahrhunderts.
* [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Mondzyklen#Der_Kalenderstein_vom_Tempel_Mnajdra|Oberste Lochreihe auf dem östlichen Kalenderstein vom Tempel '''Mnajdra''' auf Malta]].
* [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Höhlenmalerei_in_der_Magura-Höhle#Kalender|Kalender unter den Höhlenmalereien in der '''Magura'''-Höhle mit einer Matrix aus 19 Punkten]].
<gallery caption="Die Zahl 19 in archäoastronomischen Kontexten" mode=packed widths=360 heights=360>
Magura_cave_calendar_1528.jpg|Detail mit Elementen eines Kalenders aus der '''Magura'''-Höhle in Bulgarien. Unten in der Mitte befindet sich ein Feld mit 19 Punkten.
Kalenderstein.Mnajdra.labelled.png|Skizze der Lochreihen auf dem Kalenderstein von '''Mnajdra''' auf Malta. Die oberste Lochreihe besteht aus 19 Löchern.
Hattusa,_capital_of_the_Hittite_Empire_51.jpg|Detail mit den linken drei der insgesamt neunzehn Göttinnen der Bilderreihe in der Kammer A des hethitischen Heiligtums '''Yazılıkaya'''.
Yazilikaya.19.Gottheiten.png|Schmatische Darstellung der 19 weiblichen Gottheiten im hethitischen Heiligtum '''Yazılıkaya''' in der Türkei. Im Original variieren die Darstellungen der einzelnen Figuren, die in unterschiedlichem Zustand erhalten sind. Über den Händen einzelner Figuren konnten verschiedene hieroglyphische Darstellungen festgestellt werden. Die erste Figur (ganz links) konnte hierdurch nur indirekt rekonstruiert werden.
Berliner.Goldhut.P1068203.jpg||'''Berliner Goldhut''' vom Ende der Bronzezeit (1000–800 vor Christus) im Neuen Museum.
MesoamerikanischerMond.CodexBorbonicus.png|Mond-Sonne-Darstellung aus dem mesoamerikanischen '''Codex Borbonicus''' der Azteken vom Anfang des 16. Jahrhunderts. Die beiden sich zu einem vollen Kreis ergänzenden Halbkreise symbolisieren den Mond und die Sonne. Im Halbkreis des Mondes befinden sich '''19 Kreise'''.
</gallery>
====MUL.APIN====
[[Datei:MUP.APIN.VAT09412.P1078712.jpg|thumb|1914 gefundene Tontafel mit Texten aus dem 687 vor Christus geschrieben babylonischen Kompendium MUL.APIN, Irak, Assur/Qal'at Scherqat, VAT 09412, Neues Museum Berlin]]
Die auf den MUL.APIN-Tafeln schriftlich überlieferten babylonischen [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Plejaden#Schaltregeln|'''Plejaden-Schaltregeln''']] beziehen sich zweifelsfrei auf einen Zeitpunkt, als der offene Sternhaufen der Plejaden im Sternbild Stier (Taurus) die gleiche ekliptikale Länge wie der Frühlingspunkt gehabt hat. Dies bedeutet, dass die Sonne zur Tag-und-Nacht-Gleiche direkt neben den Plejaden stand, und dies war um 2600 vor Christus der Fall. Aus beiden Schaltregeln ergibt sich, dass innerhalb von 19 Sonnenjahren sieben zusätzliche synodische Monate eingeschaltet werden müssen, damit Sonnen- und synodisches Mondjahr synchron bleiben. Die Differenz zwischen Mondlauf und Sonnenlauf beträgt nach diesen 19 Sonnenjahren nur gut zwei Stunden. Diese Abweichung beträgt nur 0,0125 Promille beziehungsweise 12,58 parts per million (ppm):
* Tropisches Sonnenjahr (von Frühlingspunkt zu Frühlingspunkt): <math>J_S = 365,2422 \text { d} = 8765,813 \text { h}</math>
* Synodischer Monat (von Neumond zu Neumond): <math>M_{syn} = 29,53059 \text { d} = 708,7342 \text { h}</math>
* Synodisches Mondjahr (zwölf synodische Monate): <math>J_{syn} = 12 \cdot M_{syn} = 354,3671 \text { d} = 8504,810 \text { h}</math>
* Jahresdifferenz zwischen tropischem Sonnenjahr und synodischem Mondjahr: <math>\Delta J = J_S - J_{syn} = 10,8751 \text { d} = 261,003 \text { h}</math>
* Meton-Zyklus: <math>19 \cdot \Delta J = 206,627 \text { d} = 4959,046 \text { h}</math> beziehungsweise <math>7 \cdot M_{syn} = 206,714 \text { d} = 4961,139 \text { h}</math>
* Genauigkeit des Meton-Zyklus: <math>\Delta M = 7 \cdot M_{syn} - 19 \cdot \Delta J = 0,087 \text { d} \approx 2,09 \text { h} = 235 \cdot M_{syn} - 19 \cdot J_S</math>
* Arithmetische Zusammensetzung und Primfaktorenzerlegung der Anzahl synodischer Mondzyklen: <math>235 = 7 + 19 \cdot 12 = 5 \cdot 47</math>
Mit anderen Worten war der Meton-Zyklus bereits über zwei Jahrtausende lang bekannt, bevor sich der griechische Astronom Meton damit beschäftigte.
Es ist äußerst bemerkenswert, dass eine entsprechende Berechnung auch für dreizehn siderische Monate durchgeführt werden kann, bei denen der Mond jeweils einmal vollständig die Ekliptik mit ihren zwölf Ekliptiksternbildern beziehungsweise den 27 oder 28 Mondstationen (auch Mondhäuser genannt, siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Konjunktionen#Mondhäuser|Exkurs '''Mondhäuser''']]) durchläuft:
* Tropisches Sonnenjahr (von Frühlingspunkt zu Frühlingspunkt): <math>J_S = 365,2422 \text { d} = 8765,813 \text { h}</math>
* Siderischer Monat (von Fixstern zu Fixstern): <math>M_{sid} = 27,32166 \text { d} = 655,720 \text { h}</math>
* Siderisches Mondjahr (13 siderische Monate): <math>J_{sid} = 13 \cdot M_{sid} = 355,1816 \text { d} = 8524,358 \text { h}</math>
* Jahresdifferenz zwischen tropischem Sonnenjahr und siderischem Mondjahr: <math>\Delta J = J_S - J_{sid} = 10,0606 \text { d} = 241,454 \text { h}</math>
* Meton-Zyklus: <math>19 \cdot \Delta J = 191,151 \text { d} = 4587,634 \text { h}</math> beziehungsweise <math>7 \cdot M_{sid} = 191,252 \text { d} = 4590,040 \text { h}</math>
* Genauigkeit des Meton-Zyklus: <math>\Delta M = 7 \cdot M_{sid} - 19 \cdot \Delta J = 0,100 \text { d} \approx 2,40 \text { h} = 254 \cdot M_{sid} - 19 \cdot J_S</math>
* Arithmetische Zusammensetzung und Primfaktorenzerlegung der Anzahl siderischer Mondzyklen: <math>254 = 7 + 19 \cdot 13 = 2 \cdot 127</math>
Auch hier beträgt die Differenz zwischen Mondlauf und Sonnenlauf nur gut zwei Stunden. Und innerhalb der 19 Sonnenjahre eines Meton-Zyklus müssen auch hier sieben zusätzliche (vierzehnte) siderische Monate eingeschaltet werden, damit Sonnen- und siderisches Mondjahr synchron bleiben.
Zur Differenz von knapp elf Tagen zwischen Sonnen- und Mondjahr siehe auch '''[[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Plejaden#Das_babylonische_Neujahrsfest_Akiti|das babylonische Neujahrsfest Akiti]]'''. Dieses elftägige Fest wurde am Ende des Mondjahres gefeiert.
===Der drakonitische Zyklus===
Ferner existiert ein zirka '''18,61-jähriger Mondzyklus''', der darauf beruht, dass bedingt durch die Präzession der Mondbahn der aufsteigende und der absteigende Mondknoten nach dieser Zeit die Ekliptik entgegen der rechtläufigen (prograden) Umlaufrichtung des Mondes genau einmal vollständig rückläufig (retrograd) durchlaufen haben. Dieser Zyklus besteht aus 249,83 drakonitischen Monaten, die insgesamt 6798,38 Tagen beziehungsweise 18,61 tropischen Sonnenjahren entsprechen. Die ekliptikalen Längen der Mondknoten vermindern sich hierbei um einen Winkel von 19,34 Bogengrad pro Jahr.
Dieser drakonitische Zyklus ist zum Beispiel anhand der Abweichungen der ekliptikalen Breiten des Mondes und somit der Azimute bei den monatlichen Mondaufgängen und Monduntergängen am Horizont zu beobachten, die sich nach 18,61 Jahren wiederholen und dabei um die Punkte der Wintersonnenwende im Südosten und Südwesten sowie die Punkte der Sommersonnenwende im Nordosten und Nordwesten entlang dem Horizont pendeln. Die Zeitpunkte an dem die entsprechenden Auf- und Untergangspunkte zwischen dem nördlichen und dem südlichen Horizont um die Punkte der Tag-und-Nacht-Gleichen im Osten und Westen, die definitionsgemäß bei der ekliptikalen Breite null genau in der Ekliptik liegen, am engsten beziehungsweise am weitesten auseinanderliegen, heißen '''große und kleine Mondwenden'''. Diese Mondwenden wiederholen sich also alle 18,61 Jahre. Hierbei treten auch größte und kleinste Mondwenden auf. Um die Wintersonnenwenden der Jahre 2023, 2042, 2061 und so weiter treten beispielsweise '''größte Mondwenden''' auf, bei denen die Auf- und Untergangspunkte maximal in Richtung Norden von den genau im Osten und im Westen liegenden Äquinoktialpunkten entfernt sind. Und um die Sommersonnenwenden der Jahre 2024, 2043, 2062 und so weiter treten '''kleinste Mondwenden''' auf, bei denen die Auf- und Untergangspunkte maximal in Richtung Süden von den genau im Osten und im Westen liegenden Äquinoktialpunkten entfernt sind.
[[Datei:Ekliptik.helle.Objekte.png|mini|hochkant=2|rechts|Die sieben hellsten feststehenden Himmelsobjekte in der Nähe der Ekliptik liegen zwischen den Sternbildern Stier (Taurus, rechts) und Skorpion (Scorpio, links). Der Bogen der Ekliptik wird von den Wandelgestirnen entgegen dem Uhrzeigersinn vom Frühlingspunkt rechts zum Herbstpunkt links durchlaufen. In der Nähe unteren Bogenhälfte befinden sich keine hellen Fixsterne in der Nähe der Ekliptik, der helle Stern Fomalhaut (α Piscis Austrini) im Sternbild Südlicher Fisch (Piscis Austrinus) dient lediglich zur Orientierung. Außerhalb des Bogens liegende Punkte befinden sich nördlich der Ekliptik und innen liegende südlich.]]
Aufgrund dieser Zusammenhänge werden alle möglichen Positionen des Mondes in Bezug auf die Ekliptik bei den ekliptikalen Längen von -180 bis +180 Bogengrad und den ekliptikalen Breiten von ungefähr -6 bis +6 Bogengrad innerhalb dieser 18,61-jährigen Periode erreicht. Somit erfolgen auch alle möglichen Sternbedeckungen (Okkultationen) oder nahe Konjunktionen innerhalb dieser Periodendauer und wiederholen sich danach im drakonitischen Zyklus. Die Bedeckungen hellsten ekliptiknahen Himmelsobjekte sind hierbei besonders spektakulär und gut zu beobachten. Dies gilt insbesondere für:
* die '''Plejaden''' (Messier 45, 1,5<sup>m</sup>) im Sternbild Stier (Taurus)
* die '''Hyaden''' (0,5<sup>m</sup>) im Sternbild Stier (Taurus)
* den Roten Riesen '''Aldebaran''' (α Tauri, 1,0<sup>m</sup>) im Sternbild Stier (Taurus)
* den Roten Überriesen '''Antares''' (α Scorpii, 1,0<sup>m</sup>) im Sternbild Skorpion (Scorpio),
* den Stern '''Spica''' (α Virginis, 1,0<sup>m</sup>) im Sternbild Jungfrau (Virgo)
* den Stern '''Regulus''' (α Leonis, 1,5<sup>m</sup>) im Sternbild Löwe (Leo)
Wenn der Mond bei der Bedeckung der '''Plejaden''' seine maximale nördliche ekliptikale Breite bereits zuvor erreicht hatte und sich also bereits wieder in Richtung seines absteigenden Knotens bewegt, befindet sich in der Nähe des absteigenden Knotens der Königsstern '''Regulus''' im Sternbild Löwe (Leo), so dass es ungefähr eine Woche später ebenfalls zu dessen Bedeckung durch den Mond kommen kann. Da der Stern '''Antares''' α Scorpii) im Sternbild Skorpion (Scorpio) in etwa die gleiche südliche ekliptikale Breite hat, wie die Plejaden eine nördliche ekliptikale Breite haben, und beide auf gegenüberliegenden Punkten der Ekliptiklinie liegen, kommt es im Rhythmus des drakonitischen Zyklus innerhalb eines halben Monats gehäuft zu der Bedeckung beider Himmelsobjekte durch die Mondscheibe. Beide Ereignisse wären wegen der gegenüberliegenden Lage auf der Ekliptik heute allerdings nur während der langen Nächte im Winterhalbjahr zu sehen, wenn Antares praktisch nicht zu beobachten ist, weil er von der Sonne überstrahlt wird. Als der Frühlingspunkt im Altertum jedoch im Sternbild Stier stand, war die aufeinanderfolgende Bedeckung von Plejaden und Antares jedoch durchaus ein zu beobachtendes Doppelereignis.
Das nächste Mal werden die beiden eng benachbarten Elternsterne der Plejaden (der Titan Atlas und die Okeanide Pleione) von Mitteleuropa aus gesehen in den Morgenstunden des 8. Augusts 2024 von der Scheibe des abnehmenden Halbmonds bedeckt. Am 1. April 2025 werden gegen Mitternacht dann sogar mehrere helle Sterne des Sternhaufens durch die nur vier Tage alte Mondsichel bedeckt.
Auch im alten chinesischen, mündlich überlieferten Volksmärchen „Morgenhimmel“ wird der Zyklus vom '''Stern des großen Jahres''' erwähnt, der sich erst nach 18 Jahren, also im 19. Jahr wiederholt:<ref>[[s:Morgenhimmel|Morgenhimmel]], Wikisource</ref>
<blockquote>
Als Morgenhimmel gestorben war, berief der Kaiser den Sterndeuter und fragte: „Kanntest du Morgenhimmel?“<br/>
Der sagte: „Nein.“<br/>
Der Kaiser fragte: „Was verstehst du denn?“<br/>
Der Sterndeuter sagte: „Ich kann nach den Sternen sehen.“<br/>
„Sind alle Sterne an ihrem Platz?“ fragte der Kaiser.<br/>
„Ja. Nur den Stern des großen Jahres habe ich achtzehn Jahre nicht gesehen. Jetzt aber ist er wieder sichtbar.“<br/>
Da blickte der Kaiser zum Himmel auf und seufzte: „Achtzehn Jahre lang war Morgenhimmel mir zur Seite, und ich wusste nicht, dass er der Stern des großen Jahres war.“
</blockquote>
Mit "Stern des großen Jahres" könnte ein Ereignis gemeint sein, bei dem der Mond alle 18,61 Jahre einen bestimmten hellen und ekliptiknahen Stern bedeckt, wie zum Beispiel einen der drei Königssterne Aldebaran (α Tauri) im Sternbild Stier (Taurus), Regulus (α Leonis) im Sternbild Löwe (Leo), Antares (α Scorpii) im Sternbild Skorpion (Scorpio) oder auch Spica (α Virginis) im Sternbild Jungfrau (Virgo).
Auch für den drakonitischen Zyklus kann für dreizehn drakonitische Monate eine Berechnung in Bezug auf den 19-jährigen Meton-Zyklus durchgeführt werden:
* Tropisches Sonnenjahr (von Frühlingspunkt zu Frühlingspunkt): <math>J_S = 365,2422 \text { d} = 8765,813 \text { h}</math>
* Drakonitischer Monat (von Mondknoten zu Mondknoten): <math>M_{dra} = 27,21222 \text { d} = 653,093 \text { h}</math>
* Drakonitisches Mondjahr (13 drakonitische Monate): <math>J_{dra} = 13 \cdot M_{dra} = 353,7589 \text { d} = 8490,213 \text { h}</math>
* Jahresdifferenz zwischen tropischem Sonnenjahr und drakonitischem Mondjahr: <math>\Delta J = J_S - J_{dra} = 11,4833 \text { d} = 275,600 \text { h}</math>
* Meton-Zyklus: <math>19 \cdot \Delta J = 218,1835 \text { d} = 5236,403 \text { h}</math> beziehungsweise <math>8 \cdot M_{sid} = 217,6978 \text { d} = 5224,746 \text { h}</math>
* Genauigkeit des Meton-Zyklus: <math>\Delta M = 8 \cdot M_{dra} - 19 \cdot \Delta J = -0,486 \text { d} \approx -11,66 \text { h} = -(255 \cdot M_{dra} - 19 \cdot J_S)</math>
* Arithmetische Zusammensetzung und Primfaktorenzerlegung der Anzahl drakonitischer Mondzyklen: <math>255 = 8 + 19 \cdot 13 = 3 \cdot 5 \cdot 17</math>
Hier beträgt die Differenz zwischen Mondlauf und Sonnenlauf knapp einen halben Tag. Innerhalb der 19 Sonnenjahre eines Meton-Zyklus müssen hier acht zusätzliche (vierzehnte) drakonitische Monate eingeschaltet werden, damit Sonnen- und drakonitisches Mondjahr synchron bleiben.
===Der Saros-Zyklus===
[[Datei:Saros-Zyklus.2023.Januar.png|rechts|mini|hochkant=4|Der '''Saros-Zyklus''' umfasst sowohl eine Periode von 242 drakonitischen Monaten als auch eine Periode von 223 synodischen Monaten. Das sind gut 6585 Tage oder 18,03 tropische Sonnenjahre, also 18 Jahre und knapp 11 Tage). In der Graphik ist die Zeit vom aufsteigenden Knoten (rot) am 1. Januar 2023 bis zum aufsteigenden Knoten (rot) am 11. Januar 2041 dargestellt. Man beachte die gleichzeitige zyklische Wiederholung der jeweiligen Mondphasen sowie daraus folgend der '''Finsternisse''' im April und Mai sowie im Oktober und November mit einer Verschiebung von elf Kalendertagen im Sonnenjahr.<br/>Die '''Mondwenden''' (die tagesaktuellen Auf- und Untergangsazimute des Mondes sind im unteren Bereich für die geographische Breite 52,5 Bogengrad (zum Beispiel in Berlin) dargestellt) haben ihr Minimum und Maximum demzufolge zu den entsprechenden Zeitpunkten, die Ausprägung dieser Extrema unterscheidet sich jedoch ein wenig, weil der Mond dort geringfügig abweichende minimale und maximale ekliptikale Breiten hat. Die kleinen Mondwenden beim Sommervollmond im Juli und die großen Mondwenden beim Wintervollmond (Jahreswechsel) sind in der Graphik ebenfalls nachvollziehbar.<br/>[[Datei:Legende.Meton-Zyklus.png|200px|zentriert|Legende]]]]
Über die weiter oben beschriebenen Koinzidenzen hinaus kann beobachtet werden, dass der Mond nach '''18,03 Jahren''' (also nach 242 drakonitischen Monaten beziehungsweise 6585,3 Tagen) denselben auf- oder absteigenden Knoten erreicht, wobei Sonne und Mond dann die gleiche Elongation haben (nach 223 synodischen Monaten beziehungsweise 6585,2 Tagen). Sie befinden sich dann allerdings nur fast bei den gleichen ekliptikalen Längen beziehungsweise an den gleichen Stellen des Fixsternhimmels, da diese Dauer nur mit ungefähr einem halben Tag Differenz mit 241 siderischen Perioden übereinstimmt (6584,6 Tage). Innerhalb dieses halben Tages hat sich die Sonne um zirka ein halbes Bogengrad und der Mond sogar um ungefähr sechseinhalb Bogengrad weiterbewegt.
Dieser Zyklus wird '''Saros-Zyklus''' genannt. Innerhalb dieser Zeitspanne ergibt sich eine Reihe von Sonnen- und Mondfinsternissen, die sich in ihrer Abfolge immer wieder ähneln. Der Saros-Zyklus war bei den Babyloniern bereits im Altertum bekannt, was in Keilschrift geschrieben auf Tontafeln aus der Mitte des achten vorchristlichen Jahrhunderts überliefert ist.
Heute werden die sich wiederholenden Sonnen- und Mondfinsternisse, die sich auf einen bestimmten auf- oder absteigenden Knoten der Mondbahn beziehen und jeweils eine Lebensdauer von mehreren Jahrhunderten haben, zum Zwecke der Unterscheidung mit Nummern versehen. Zu einem bestimmten Zeitpunkt der Weltgeschichte sind immer mehrere Dutzend Saros-Zyklen gleichzeitig aktiv.
Die anomalistische Periode steht für die Dauer, bis der Mond auf seiner elliptischen Bahn um die Erde erneut eines seiner beiden Apogäen erreicht, also das Perigäum (erdnächster Punkt) oder das Apogäum (erdfernster Punkt). Nach einer ganzzahligen vielfachen Dauer der anomalistischen Periode hat der Mond also stets fast die gleiche Entfernung zur Erde und somit auch die gleiche scheinbare Größe, so dass die Dauer und Art (totale oder ringförmige Sonnenfinsternis) einer Finsternis sich wiederholt.
Wegen der gleichen ekliptikalen Breiten des Mondes nach einer ganzzahligen vielfachen Dauer der drakonitischen Periode erscheint der Mondschatten auf der Erdoberfläche nach der Dauer einer Saros-Periode ungefähr beim gleichen Breitengrad. Durch die Differenz von rund einem drittel Tag ist der Längengrad allerdings nach einer Saros-Periode um knapp 120 Bogengrad nach Westen verschoben. Nach drei Saros Perioden, oder nach einem dadurch definierten Exeligmos-Zyklus, haben sich die drei Dritteltage fast auf einen Tag summiert, so dass nach der Mondschatten dann nicht nur wieder beim ähnlichen Breitengrad, sondern sogar beim ähnlichen Längengrad und zu einer ähnlichen Tageszeit auftaucht. Anders ausgedrückt: wenn an einem bestimmten Ort auf der Erdoberfläche eine Finsternis auftritt, so erfolgt in der Regel nach 19756 Tagen (54 Jahren und 33 Tagen) in dieser geographischen Region mit nur rund einer Stunde Verfrühung erneut eine solche Finsternis.
{| class="wikitable"
|+ Exeligmos-Zyklus
! Anzahl der Saros-Zyklen !! Dauer in tropischen Jahren !! Dauer in ganzen Tagen
|-
| 1 || 18,03 || 6585
|-
| 3 || 54,09 || 19756
|}
Die folgende Tabelle gibt einen Überblick über die Dauern verschiedener Mondzyklen in einem '''Saros-Zyklus''' (18,03 tropische Jahre) sowie in einem dreimal so langen Zyklus '''Exeligmos-Zyklus''' (54,09 tropische Jahre) .
{| class="wikitable"
!
!Synodische Perioden
!Drakonitische Perioden
!Tage
!Anomalistische Perioden
|-
|'''Dauer einer Periode in Tagen'''
|'''29,53059'''
|'''27,21222'''
|'''1'''
|'''27,53455'''
|-
|
|-
|'''Anzahl der Perioden im Saros-Zyklus'''
|'''223'''
|'''242'''
|'''6585'''
|'''239'''
|-
|'''Anzahl der Tage im Saros-Zyklus'''
|6585,3216
|6585,3572
|6585,0000
|6580,7575
|-
|'''Differenz zu synodischen Perioden in Tagen'''
|0,0000
|0,0357
| -0,3216
| -4,5641
|-
|'''Differenz zu drakonitischen Perioden in Tagen'''
| -0,0357
|0,0000
| -0,3572
| -4,5998
|-
|'''Differenz zu ganzen Tagen'''
|0,3216
|0,3572
|0,0000
| -4,2425
|-
|'''Differenz zu synodischen Perioden in Stunden'''
|0,00
|0,86
| -7,72
| -109,54
|-
|'''Differenz zu drakonitischen Perioden in Stunden'''
| -0,86
|0,00
| -8,57
| -110,39
|-
|'''Differenz zu ganzen Tagen in Stunden'''
|7,72
|8,57
|0,00
| -101,82
|-
|
|-
|'''Anzahl der Perioden im Exeligmos-Zyklus'''
|'''669'''
|'''726'''
|'''19756'''
|'''717'''
|-
|'''Anzahl der Tage im Exeligmos-Zyklus'''
|19755,9647
|19756,0717
|19756,0000
|19742,2724
|-
|'''Differenz zu synodischen Perioden in Tagen'''
|0,0000
|0,1070
|0,0353
| -13,6924
|-
|'''Differenz zu drakonitischen Perioden in Tagen'''
| -0,1070
|0,0000
| -0,0717
| -13,7994
|-
|'''Differenz zu ganzen Tagen in Tagen'''
| -0,0353
|0,0717
|0,0000
| -13,7277
|-
|'''Differenz zu synodischen Perioden in Stunden'''
|0,00
|2,57
|0,85
| -328,62
|-
|'''Differenz zu drakonitischen Perioden in Stunden'''
| -2,57
|0,00
| -1,72
| -331,18
|-
|'''Differenz zu ganzen Tagen in Stunden'''
| -0,85
|1,72
|0,00
| -329,46
|}
===Mondwenden===
Da sich die '''Sonne''' definitionsgemäß immer in der Ekliptikebene befindet, wiederholen sich die Verhältnisse in Bezug auf ihre Auf- und Untergangspunkte sowie auf ihre südlichen und die nördlichen Kulminationshöhen in jedem tropischen Sonnenjahr. Zur '''Sommersonnenwende''' steht die Sonne am höchsten und am längsten über den Horizont, die Auf- und Untergangsazimute erreichen ihre nördlichsten Positionen und es ergibt sich der Kalendertag mit dem längsten Tagesslicht und der kürzesten Dunkelheit. Zur '''Wintersonnenwende''' steht die Sonne am niedrigsten und am kürzesten über den Horizont, die Auf- und Untergangsazimute erreichen ihre südlichsten Positionen und es ergibt sich der Kalendertag mit dem kürzesten Tagesslicht und der längsten Nacht. Bei den beiden '''Äquinoktien''' zum '''Frühlings- und Herbstbeginn''' herrscht die '''Tag-und-Nacht-Gleiche''', zwölf Stunden Tag und zwölf Stunden Nacht. Die Sonne geht überall auf der Erde exakt im Osten auf beziehungsweise im Westen unter.
Mit dem Neumond verhält es sich ähnlich wie mit der Sonne, da er sich stets in Sonnennähe befindet. Die Mondbahn ist gegenüber der Ekliptik jedoch geneigt (Inklination der Mondbahn), so dass der Mond bis zu gut 5 Bogengrad höher oder niedriger stehen kann als die Sonnenscheibe. Bei einer '''großen Mondwende''' steht die Ekliptik immer besonders hoch über dem Horizont und der Mond hat gleichzeitig seine maximale nördliche ekliptikale Breite erreicht. Wenn der Vollmond bei einer großen Mondwende auf dem südlichen Meridian kulminiert, findet dieses Ereignis als '''größte Mondwende''' um Mitternacht zur Wintersonnenwende statt (im Dezember 2023, 2042 und 2061).
[[Datei:Grosse.Mondwende.png|mini|zentriert|hochkant=2|Die größte Mondwende bei der am höchsten stehenden Ekliptik auf der Nordhalbkugel sowie die Sommersonnenwende im Horizontsystem. Die blaue Mondbahn wird während der Sommersonnenwende nur bei Neumond gleichzeitig mit der orangefarbenen Sonnenbahn durchlaufen. Während der Wintersonnenwende findet diese große Mondwende statt, wenn der Mond in Opposition zur Sonne steht und somit Vollmond herrscht.]]
Bei einer '''kleinen Mondwende''' steht die Ekliptik immer besonders niedrig über dem Horizont und der Mond hat gleichzeitig seine minimale südliche ekliptikale Breite erreicht. Wenn der Vollmond bei einer kleinen Mondwende auf dem südlichen Meridian kulminiert, findet dieses Ereignis als '''kleinste Mondwende''' um Mitternacht zur Sommersonnenwende statt (im Juni 2024, 2043 und 2062).
[[Datei:Kleine.Mondwende.png|mini|zentriert|hochkant=2|Die kleinste Mondwende bei der am niedrigsten stehenden Ekliptik auf der Nordhalbkugel sowie die Wintersonnenwende im Horizontsystem. Die blaue Mondbahn wird während der Wintersonnenwende nur bei Neumond gleichzeitig mit der orangefarbenen Sonnenbahn durchlaufen. Während der Sommersonnenwende findet diese kleine Mondwende statt, wenn der Mond in Opposition zur Sonne steht und somit Vollmond herrscht.]]
[[Datei:ZunehmenderMond.Herbst-Aequinoktium.P1185517.jpg|mini|rechts|Zunehmender Mond zum Herbst-Äquinoktium bei kleiner Mondwende mit flacher Ekliptik in Berlin mit fast senkrecht stehender Mondsichel (38% beleuchtet) im Sternbild Skorpion kurz vor Sonnenuntergang. Visuelle Helligkeit -9 mag, südliche ekliptikale Breite = -5°, Höhe über dem südlichen Horizont = 9°, Mondalter 6,2 Tage.]]
In der folgenden Übersicht sind die '''Mondaufgangspunkte''' am östlichen Horizont, die '''Kulminationspunkte''' auf dem südlichen Meridian und die '''Monduntergangspunkte''' am westlichen Horizont für die vier verschiedenen Jahreszeiten angegeben:
{| class="wikitable"
|+ Die Lage des Bogens der Ekliptik über dem nächtlichen Horizont zu verschiedenen Jahreszeiten
!title="Jahreszeit"| Jahreszeit
!title="Am Abend"| Am Abend</br>(Aufgang des Vollmonds</br>in östlicher Richtung)
!title="Um Mitternacht"| Um Mitternacht</br>(Kulmination des Vollmonds</br>auf dem südlichen Meridian)
!title="Am Morgen"| Am Morgen</br>(Untergang des Vollmonds</br>in westlicher Richtung)
|-
| '''Frühlingsanfang'''</br>'''Tag-und-Nacht-Gleiche''' || [[Datei:Ekliptik.C.png|360px]] || [[Datei:Ekliptik.D.png|360px]] || [[Datei:Ekliptik.A.png|360px]]
|-
| '''Sommeranfang'''</br>'''Sommersonnenwende''' || [[Datei:Ekliptik.D.png|360px]] || [[Datei:Ekliptik.A.png|360px]] || [[Datei:Ekliptik.B.png|360px]]
|-
| '''Herbstanfang'''</br>'''Tag-und-Nacht-Gleiche''' || [[Datei:Ekliptik.A.png|360px]] || [[Datei:Ekliptik.B.png|360px]] || [[Datei:Ekliptik.C.png|360px]]
|-
| '''Winteranfang'''</br>'''Wintersonnenwende''' || [[Datei:Ekliptik.B.png|360px]] || [[Datei:Ekliptik.C.png|360px]] || [[Datei:Ekliptik.D.png|360px]]
|}
[[Datei:Orbit de Mondknoten.svg|mini|rechts|hochkant=2|Bahnellipse des Mondes mit den beiden Mondknoten.]]
Im Laufe eines '''drakonitischen Monats''' geht der Mond sowohl durch den aufsteigenden als auch durch den absteigenden Knoten (Drachenpunkte) seiner Mondbahn. Nach dem Durchqueren des aufsteigenden Knotens wird die ekliptikale Breite des Mondes positiv (nördliche ekliptikale Breite), er steigt in Bezug zur Ekliptiklinie nach Norden und bleibt entsprechend immer länger über dem Horizont. Nach dem Durchqueren des absteigenden Knotens wird die ekliptikale Breite des Mondes negativ (südliche ekliptikale Breite), er fällt in Bezug zur Ekliptiklinie nach Süden und bleibt entsprechend immer kürzer über dem Horizont. In der deutschsprachigen Schweiz gibt es für diese im Laufe eines drakonitischen Monats täglich mehr oder weniger deutlich wahrnehmbaren Änderungen der ekliptikalen Breite sogar eigene Adjektive. Das Ansteigen der ekliptikalen Breite des Mondes nach Norden wird '''obsigend''', und das Abfallen der ekliptikalen Breite des Mondes nach Süden wird '''nidsigend''' genannt.
[[Datei:Mond-.und.Sonnenwenden.png|mini|rechts|hochkant=2|Verschiedene Mond- und Sonnenwenden auf dem südlichen Meridian im Laufe eines Jahres in Bezug zum Horizontsystem.]]
Im Laufe der Zeit pendelt der Mond immer wieder zwischen den kleinen und großen Mondwenden. Dieser Ablauf wiederholt sich nach einem vollständigen '''drakonitischen Zyklus''', also nach 18,61 Jahren, innerhalb dessen die kleinste und die größte Mondwende im zeitlichen Abstand von rund 9,3 Jahren jeweils einmal erreicht wird.
Die Deklinationen der Sonne <math>\delta_S</math> pendeln also je nach Jahreszeit maximal um den Betrag der Schiefe der Ekliptik <math>\varepsilon</math> um den Nullpunkt <math>\delta_S = 0</math>, der nur zu den Tag-und-Nacht-Gleichen erreicht wird:
:<math>-\varepsilon \le \delta_S \le +\varepsilon</math>
Die Deklinationen des Mondes <math>\delta_M</math> pendeln zusätzlich um den Betrag der Inklination der Mondbahn <math>i</math> (heute '''5,145 Bogengrad''') um die Werte der Deklination der Sonne <math>\delta_S</math>:
:<math>-\varepsilon - i \le \delta_M \le +\varepsilon + i</math>
Die Deklination und der maximale Höhenwinkel bei der Kulmination des Mondes auf dem südlichen Meridian variieren also sowohl innerhalb eines Monats durch die Inklination der Mondbahn als auch jährlich durch die Neigung der Erdachse (also die Schiefe der Ekliptik).
Die für die Azimute der Mondaufgänge <math>a_{A,M}</math> anzuwendende Formel hängt nicht nur von der augenblicklichen Deklination des Mondes <math>\delta_M</math>, sondern auch vom geographischen Breitengrad des Beobachters <math>\varphi</math> ab:
:<math>\cos a_{A,M} = \frac {\sin \delta_M} {\cos \varphi}</math>
Aus Symmetriegründen ergibt sich für die Azimute der Monduntergänge <math>a_{U,M}</math>:
:<math>a_{U,M} = 360^\circ - a_{A,M} = 360^\circ - \arccos \frac {\sin \delta_M} {\cos \varphi}</math>
{| class="wikitable"
|+ Deklinationen des Mondes
! Ereignis !! Deklination des Mondes <math>\delta_M</math> !! Maximaler Höhenwinkel des Mondes<br/><math>h_{max,M}</math> || Azimut des Mondaufgangs<br/><math>a_{A,M}</math> || Azimut des Monduntergangs<br/><math>a_{U,M}</math>
|-
| Kleinste Mondwende<br/><math>\delta_S = -\varepsilon</math> || <math>-(\varepsilon + i)</math> || <math>(90^\circ - \varphi) - (\varepsilon + i)</math> || <math>\arccos \frac {\sin (-\varepsilon - i)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (-\varepsilon - i)} {\cos \varphi}</math>
|-
| Kleine Mondwende || <math>\delta_S - i</math> || <math>(90^\circ - \varphi) + (\delta_S - i)</math> || <math>\arccos \frac {\sin (\delta_S - i)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (\delta_S - i)} {\cos \varphi}</math>
|-
| Mond im Knoten || <math>\delta_S</math> || <math>(90^\circ - \varphi) + \delta_S</math> || <math>\arccos \frac {\sin (\delta_S)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (\delta_S)} {\cos \varphi}</math>
|-
| Große Mondwende || <math>\delta_S + i</math> || <math>(90^\circ - \varphi) + (\delta_S + i)</math> || <math>\arccos \frac {\sin (\delta_S + i)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (\delta_S + i)} {\cos \varphi}</math>
|-
| Größte Mondwende<br/><math>\delta_S = +\varepsilon</math> || <math>+(\varepsilon + i)</math> || <math>(90^\circ - \varphi) + (\varepsilon + i)</math> || <math>\arccos \frac {\sin (\varepsilon + i)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (\varepsilon + i)} {\cos \varphi}</math>
|}
Die Höhenwinkel des Mondes und der Sonne konnten zum Beispiel schon in der Bronzezeit mit Hilfe von '''Sonnenuhren''', '''Stabdolchen''' oder '''Kreisgrabenanlagen''' beobachtet und bestimmt werden. Im Laufe eines Jahres konnten damit die jahreszeitlichen Veränderungen der scheinbaren Sonnenbahn verfolgt werden. Durch eine langjährige Beobachtung war es dann auch möglich, die kleinsten und größten Mondwenden zu ermitteln.
Siehe hierzu auch:
* → '''[[Quadriviale_Kuriositäten/_Stabdolche|Stabdolche]]'''
* → '''[[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Kalenderführung#Frühgeschichtliche_Observatorien|Frühgeschichtliche Observatorien]]'''
===Vergleich der Perioden===
Die folgenden Periodendauern spielen bei Sonnen- und Mondzyklen eine besondere Rolle:
* '''Meton-Zyklus:'''
** 19 '''tropische Sonnenjahre''' = 6939,6 Tage, bestimmt durch die ekliptikale Länge der Sonne in Bezug zum Frühlingspunkt mit ihren Äquinoktien und Sonnenwenden.
** 235 '''synodische Monate''' = 6939,7 Tage, bestimmt durch die Mondphasen mit Neumonden und Vollmonden.
** 254 '''siderische Monate''' = 6939,7 Tage, bestimmt durch die ekliptikale Länge des Mondes in Bezug zum Frühlingspunkt und den Fixsternhimmel.
** 255 '''drakonitische Monate''' = 6939,1 Tage, bestimmt durch die ekliptikale Breite des Mondes beziehungsweise die auf- und absteigenden Knoten der Mondbahn.
* '''Drakonitischer Zyklus:'''
** 249,83 '''drakonitische Monate''' = 6798,38 Tage = 18,613 tropische Sonnenjahre, bestimmt durch die ekliptikalen Längen der auf- und absteigenden Knoten der Mondbahn: Zyklus der Mondwenden und Sternbedeckungen (Nutationsperiode)
* '''Saros-Zyklus:'''
** 242 '''drakonitische Monate''' = 6585,3 Tage = 18,03 tropische Sonnenjahre (respektive 18 Jahre und 11 Tage): Zyklen der Sonnen- und Mondfinsternisse
** 223 '''synodische Monate''' = 6585,4 Tage, bestimmt durch die Mondphasen mit Neumonden und Vollmonden.
[[Datei:Meton-Zyklus.2023.4x5.png|mini|links|hochkant=4|Der 19-jährige Meton-Zyklus vom Januar 2023 bis zum Januar 2042. Die Zeit schreitet in horizontaler Richtung jeweils um einen halben Tag pro Bildpunkt voran.<br/>Im unteren Teil des Diagramms sind die kleinen und großen Mondwenden für die geographische Breite 52,5 Bogengrad (zum Beispiel in Berlin) dargestellt.]]
<div style="clear:both"></div>
[[Datei:Herbstaequinoktium.kleine.Mondwende.2033.png|mini|rechts|hochkant=1|Herbstäquinoktium (orange) und Sonnenfinsternis bei aufsteigendem (rot) Neumond am 23. September 2033 (orangefarbene Linie), Mondfinsternis bei absteigendem (blau) Vollmond am 8. Oktober 2033 und Wintersonnenwende (türkis) mit kleiner Mondwende bei Neumond am 21. Dezember 2033 (grüne Linie).]]
Der '''Vollmond''' (weiß) erscheint sowohl zu Beginn der Darstellung am 6. Januar 2023 als auch nach genau 19 Jahren am 6. Januar 2042. Dieser Sachverhalt ergibt sich daraus, dass nach dem Durchlauf eines Meton-Zyklus eine ganzzahlige Anzahl von 235 synodischen Perioden durchlaufen wird (gleiche '''Elongation''' des Mondes). Diese Vollmonde stehen bei der gleichen ekliptikalen Länge im Sternbild Zwillinge (Gemini). Dieser Sachverhalt ergibt sich daraus, dass nach dem Durchlauf eines Meton-Zyklus eine ganzzahlige Anzahl von 254 siderischen Perioden durchlaufen wird. Gleichzeitig steht auch die Sonne nach 19 tropischen Sonnenjahren auf dem Zodiak bei der gleichen ekliptikalen Länge und somit erneut genau gegenüber vom Mond im Sternbild Schütze (Sagittarius). Da die Meton-Periode nur gut einen halben Tag länger ist als 255 drakonitische Perioden, stehen diese beiden Vollmonde bei fast der gleichen ekliptikalen Breite nördlich vom Stern Mebsuta (ε Geminorum).
'''Große Mondwenden''' mit maximaler Höhe des Mondes und längstem Bogen über dem Horizont folgen stets dem aufsteigenden Mondknoten (rot), und '''kleine Mondwenden''' mit minimaler Höhe des Mondes und kürzestem Bogen über dem Horizont folgen stets dem absteigenden Mondknoten (blau).
[[Datei:KleinsteMondwende.23.6.2024.HDR.png|mini|hochkant=2|'''Kleinste Mondwende um die Mittsommernacht 2024''': Der Vollmond (scheinbarer Durchmesser = 31,8 Minuten) am 23. Juni 2024 nach ein Uhr und kurz vor der oberen Kulmination auf dem südlichen Meridian von Berlin-Lankwitz (nördliche geographische Breite = 52,44 Bogengrad) aus gesehen. Die ekliptikale Breite hatte einen Minimalwert, der fast 6 Bogengrad südliche Breite betrug und somit zu einer extrem geringen maximalen Höhe über dem Horizont von nur 8,5 Bogengrad führte. Im norwegischen Trondheim (nördliche geographische Breite = 63,44 Bogengrad, also drei Breitengrade südlich vom nördlichen Polarkreis) oder im isländischen Reykjavik (nördliche geographische Breite = 64,15 Bogengrad) war in diesen Tagen weder die Mitternachtssonne noch der Vollmond zu sehen. Der helle Stern rechts oberhalb der Mondscheibe ist Nunki im Schützen (σ Sagittarius, visuell 2,7<sup>m</sup>), unterhalb ist Asella (ζ Sagittarius, visuell 4,2<sup>m</sup>) zu sehen.]]
Die '''größte Mondwende''' bei Vollmond tritt nur zur Wintersonnenwende und die '''kleinste Mondwende''' bei Vollmond tritt nur zur Sommersonnenwende auf. Je nach ekliptikaler Breite des Mondes schwanken die absoluten Werte für die Horizontwinkel um das doppelte der Inklination der Mondbahn, das heißt insgesamt um bis zu 10,29 Bogengrad. Die ekliptikale Breite bezieht sich auf den Erdmittelpunkt, da dieser in der Ekliptikebene liegt. Die von der Erdoberfläche aus beobachtete ekliptikale Breite des im Mittel nur zirka 383100 Kilometer entfernten Mondes hängt zusätzlich vom jeweiligen geographischen Breitengrad der Beobachtung ab. Der in Bezug auf die Ekliptik nördlichste und südlichste Punkt auf der Erdoberfläche liegen um den Erdradius von gut 6350 Kilometer oberhalb (nördlich) beziehungsweise unterhalb (südlich) der Ekliptikebene. Daraus resultiert für die beobachtete ekliptikale Breite ein zusätzlicher Winkel von knapp einem Bogengrad:
:<math>\Delta \beta = \arctan \frac {\pm 6350} {383100} \approx \pm 0,95^\circ</math>
Die '''größte Mondwende bei Vollmond''' (weiß) tritt bei maximaler ekliptikaler Breite des Mondes um die Wintersonnenwende (türkis, siehe Dezember 2023) auf. Die '''kleinste Mondwende bei Vollmond''' (weiß) tritt bei minimaler ekliptikaler Breite des Mondes um die Sommersonnenwende (gelb, siehe Juni 2024).
Im 19-jährigen Diagramm (siehe oben) ist gut zu erkennen, wie sich die Konstellation zwischen Mondknoten (rot und blau) und Mondphasen (weiß und schwarz) nach den 19 Jahren des '''Meton-Zyklus''' wiederholt (vergleiche Januar 2023 und Januar 2042).
Der erste aufsteigende Mondknoten (rot) wird fünf Tage vor Vollmond am 2. Januar 2023 erreicht. Nach einer vollständigen '''Saros-Periode''' von 18 Jahren und 11 Tagen, also am 12. Januar 2041 wird fünf Tage vor Vollmond ebenfalls wieder ein aufsteigender Mondknoten erreicht.
Eine Besonderheit gibt es im März 2038: Die Tag-und-Nacht-Gleiche erfolgt bereits am 20. März 2038 (Samstag) und der erste Frühlingsvollmond nur einige Stunden später am 21. März 2038 (Sonntag). Grundsätzlich müsste also der darauffolgende Sonntag, nämlich der 28. März 2038, das '''Osterdatum''' sein. In diesem Fall greift die abweichende Regel, dass erst der Vollmond nach dem 21. März als Frühlingsvollmond zu rechnen ist, was erst am 19. April 2038 (Montag) der Fall ist. Demzufolge ist das Osterdatum auf den 25. April 2038 (Sonntag) festgelegt.
Eine '''Sonnenfinsternis '''tritt immer dann auf, wenn ein Neumond (weiß) zeitnah durch einen aufsteigenden (rot) oder absteigenden (blau) Mondknoten läuft. Dies ist im obigen Diagramm an den folgenden Tagen der Fall:
* 20. April 2023, 14. Oktober 2023, 8. April 2024, 2. Oktober 2024, 29. März 2025, '''21. September 2025''' (Herbstäquinoktium), 17. Februar 2026, 12. August 2026, 6. Februar 2027, 2. August 2027, 26. Januar 2028, 22. Juli 2028, 14. Januar 2029, 12. Juni 2029, 11. Juli 2029, 5. Dezember 2029, 1. Juni 2030, 25. November 2030, 21. Mai 2031, 14. November 2031, 9. Mai 2032, 3. November 2032, 30. März 2033, '''23. September 2033''' (Herbstäquinoktium), '''20. März 2034''' (Frühlingsäquinoktium), 12. September 2034, 9. März 2035, 2. September 2035, 27. Februar 2036, 23. Juli 2036, 21. August 2036, 16. Januar 2037, 13. Juli 2037, 5. Januar 2038, 2. Juli 2038, 26. Dezember 2038, '''21. Juni 2039''' (Sommersonnenwende), 15. Dezember 2039, 11. Mai 2040, 4. November 2040, 30. April 2041, 25. Oktober 2041
Genau neunzehn Jahre nach den ersten beiden genannten Sonnenfinsternissen im Jahr 2023 treten an den gleichen Kalendertagen erneut zwei Sonnenfinsternisse auf, nämlich am 20. April 2042 und am 14. Oktober 2042.
Neunzehn Jahre vor den sechs ersten aufgeführten Sonnenfinsternissen in den Jahren 2023 bis 2025 traten an fast immer den gleichen Kalendertagen ebenfalls Sonnenfinsternisse auf, nämlich am 19. April 2004, am 14. Oktober 2004, am 8. April 2005, am 3. Oktober 2005, am 29. März 2006 und am 22. September 2006.
Eine '''Mondfinsternis '''tritt immer dann auf, wenn ein Vollmond (weiß) zeitnah durch einen aufsteigenden (rot) oder absteigenden (blau) Mondknoten läuft. Dies ist im obigen Diagramm an den folgenden Tagen der Fall:
* 5. Mai 2023, 28. Oktober 2023, 25. März 2024, 18. September 2024, 14. März 2025, 7. September 2025, 3. März 2026, 28. August 2026, 20. Februar 2027, 18. Juli 2027, 17. August 2027, 12. Januar 2028, 6. Juli 2028, 31. Dezember 2028, 26. Juni 2029, '''20. Dezember 2029''' (Wintersonnenwende), 15. Juni 2030, 9. Dezember 2030, 7. Mai 2031, 5. Juni 2031, 30. Oktober 2031, 25. April 2032, 18. Oktober 2032, 14. April 2033, 8. Oktober 2033, 3. April 2034, 28. September 2034, 22. Februar 2035, 19. August 2035, 11. Februar 2036, 7. August 2036, 31. Januar 2037, 27. Juli 2037, 21. Januar 2038, '''17. Juni 2038''' (Sommersonnenwende), 16. Juli 2038, 11. Dezember 2038, 6. Juni 2039, 30. November 2039, 26. Mai 2040, 18. November 2040, 16. Mai 2041, 8. November 2041
<gallery caption="Finsternisse" perrow="1" widths="1024" heights="470">
Finsternisse.2025.png|Die Finsternisse im Jahr 2025 liegen in der Nähe der Tag-und-Nacht-Gleichen, wo mittlere Mondwenden vorliegen. Der Mond geht im Osten auf und im Westen unter.
Finsternisse.2029.png|Die Finsternisse im Jahr 2029 liegen in der Nähe der Sonnenwenden. Bei der Sonnenfinsternis im Juni kommt es bei Neumond zu einer großen Mondwende, und bei der darauffolgenden Mondfinsternis kommt es bei Vollmond zu einer kleinen Mondwende. Bei der Sonnenfinsternis im Dezember kommt es bei Neumond zu einer kleinen Mondwende, und bei der darauffolgenden Mondfinsternis kommt es bei Vollmond zu einer großen Mondwende.
</gallery>
==Der Kalenderstein vom Tempel Mnajdra==
Auf Malta wurde im Hypogäum von Ħal-Saflieni beim Ort Tarxien ein annähernd kreisrunder Stein aus der Tempelperiode der Insel mit zirka sechs Zentimeter Durchmesser gefunden, der wie die Darstellung einer Vollmondscheibe aussieht.<ref>Daniel Cilia: [http://web.infinito.it/utenti/m/malta_mega_temples/TempleFig/%20Pres,Misc/pages/face.htm Found in a house at Hal Saflieni, stone, c.6 cm wide], The megalithic temples of Malta - the world's most ancient stone architectur, DSCF9754</ref> Im maltesischen Tempel Mnajdra sind an der südlichen Küste Maltas zirka zehn Kilometer entfernt davon zwei große Kalendersteine gefunden worden, die ebenfalls aus dieser Zeit stammen.
<gallery caption="Die Ruinen der Tempelanlage von Mnajdra" widths="300" heights="300" mode="packed">
Plan_der_Tempel_von_Mnajdra.png|Plan der Tempelanlage. Die beiden Kalendersteine befinden sich in der Mitte des Urtempels im Osten der Anlage (Buchstabe "A"), die aus dem vierten vorchristlichen Jahrtausend stammt.
Mini_Europa_Brüssel-2125_Tempel_von_Mnajdra.jpg|Modell der Tempelanlage im Park "Mini-Europa" in Brüssel. Die Kalendersteine befinden sich links und rechts hinter der Frauenfigur auf der rechten Seite.
Malta_-_Qrendi_-_Hagar_Qim_and_Mnajdra_Archaeological_Park_-_Mnajdra_16_ies.jpg|Darstellung der Lochreihen der beiden Kalendersteine auf einer Schautafel der archäologischen Stätte.
Mnajdra_East_Temple_1_(6799974274).jpg|Blick vom Eingang des Osttempels auf die beiden aufrechtstehenden Kalendersteine – links der westliche und rechts der östliche Kalenderstein.
Mnajdra_Temple_-_Chris_Brown.jpg|Blick über die Kalendersteine – links der östliche und rechts der westliche Kalenderstein, den Eingang des Osttempels und die Südküste Maltas auf das Mittelmeer mit der vorgelagerten Insel Filfla.
</gallery>
Auf dem östlichen Kalenderstein gibt es mehrere Lochreihen, deren Lochzahlen alle mit lunaren und solaren Kalendern in Zusammenhang gebracht werden können. In der heute zusammengestellten Anordnung der Steine können nicht alle Löcher gebohrt worden sein, da diese teilweise von anderen Steinen verdeckt sind. Die Bohrungen sind heute in horizontaler Richtung ausgerichtet, wurden damals vermutlich unter Ausnutzung der Gravitation senkrecht nach unten auf dem noch liegenden Stein ausgeführt. In dieser Ausrichtung des Steins wäre es auch leicht möglich gewesen, für Markierungs- oder Zählzwecke zum Beispiel kugelförmige Gegenstände in die Löcher zu legen. Steckkalender aus Stein mit Bohrungen (bei den Griechen hießen sie '''Parapegmata''') waren im Altertum an vielen Orten in Verwendung.
Am Kopf des Steins gibt es mehrere hundert, flächenhaft angeordnete Löcher, die eventuell für die einzelnen Monate oder Jahre einer langfristigen Beobachtung stehen. Darunter tauchen rechtsbündig sieben horizontale Lochreihen auf, die in der Skizze mit den Buchstaben A bis G gekennzeichnet sind, wobei die beiden Teilreihengruppen B1 und B2 sowie C1, C2 und C3 zusammengefasst betrachtet werden:
[[Datei:Kalenderstein.Mnajdra.labelled.png|mini|links|hochkant=3|Skizze der Lochreihen auf dem Kalenderstein von Mnajdra nach Ventura und Hoskin.<ref name="Ventura">Frank Ventura, Michael Hoskin: [[doi:10.1007/978-1-4614-6141-8_133|Temples of Malta]], in: Clive Ruggles (Herausgeber), ''Handbook of Archaeoastronomy and Ethnoastronomy'', 7. Juli 2014, Seiten 1421-1430, Springer, New York, ISBN 978-1-4614-6140-1</ref>]]
{| class="wikitable"
|+ Lochreihen auf dem östlichen Kalenderstein vom Tempel Mnajdra auf Malta
!title="Reihe"| Reihe
!title="Anzahl der Löcher"| Anzahl der Löcher
!title="Mögliche Verwendung"| Mögliche Verwendung
|-
| A || 19 || Für die jeweilige Goldene Zahl jedes Sonnenjahres innerhalb des 19-jährigen '''Meton-Zyklus''' (235 synodische, 255 drakonitische, 254 siderische Monate beziehungsweise 6940 Tage).<br/>Nach einem Sonnenjahr hat die Sonne wieder die gleiche ekliptikalen Länge. Nach Ablauf der gesamten Meton-Periode hat der Mond wieder die gleiche Mondphase '''und''' die gleiche ekliptikalen Breite '''und''' die gleiche ekliptikalen Länge (zum Beispiel im Goldenen Tor der Ekliptik oder im Frühlingspunkt).
|-
| rowspan=2 | B || B<sub>1</sub>: 13 (links) || rowspan=2 | In Summe 29, für die Anzahl der vollständigen Tage in einem '''synodischen Monat''' (29,5 Tage). Nach dieser Zeit hat der Mond wieder die gleiche Mondphase erreicht.<br/>Vom Altlicht des Mondes bis zum Vollmond sind es 16 Tage, und danach sind es 13 Tage bis zum nächsten Altlicht.<br/>Nachdem die Doppelreihe vervollständigt wurde, gibt es dafür einen Übertrag in die Reihe E und wenn diese bereits voll ist, für das nächste beginnende Jahr mit der nächstfolgenden '''Goldenen Zahl''' einen Übertrag in die Reihe A.
|-
| B<sub>2</sub>: 16 (rechts darunter)
|-
| rowspan=3 | C || C<sub>1</sub>: 3 (rechts oben) || rowspan=2 | Für die sieben vollständigen Tage eines '''Mondviertels''' (≈7,4 Tage) respektive einer '''Woche'''.<br/>Wenn diese Doppelreihe gefüllt ist, gibt es für die Vervollständigung einer neuen Woche einen Übertrag in die Reihe G für die Wochen in einem Jahr.<br/>Alternativ könnten hier jeweils die drei Monate in den vier Jahreszeiten markiert und gezählt worden sein.<br/>Ein weiterer Zusammenhang besteht eventuell mit der Tatsache, dass in 19 Jahren (siehe Reihe A) sieben synodische Schaltmonate erforderlich sind, um in einem lunisolaren Kalender den Lauf von Sonne und Mond bis auf wenige Stunden genau zu synchronisieren (siehe hierzu auch''' [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Plejaden#Schaltregeln|Plejaden-Schaltregeln]]''').
|-
| C<sub>2</sub>: 4 (rechts unten)
|-
| C<sub>3</sub>: 3 (links) || Für die drei nach Neumond '''vollendeten Mondviertel''' innerhalb eines laufenden synodischen Monats.</br>Beim Erreichen eines Neumonds, eines abnehmenden Halbmonds, eines Vollmonds oder eines abnehmenden Halbmonds gibt es jeweils einen Übertrag in die Reihe D oder in die Reihe F (siehe unten).
|-
| D || 25 || Für die 25 '''vollendeten Mondviertel''' in der ersten Hälfte eines Sonnenjahres, oder entweder für alle '''zunehmenden Mondviertel''' (wenn der Mond zu Beginn des Jahres zunehmend war) oder für alle '''abnehmenden Mondviertel''' (wenn der Mond zu Beginn des Jahres abnehmend war) eines Sonnenjahres (vergleiche Reihe F).
|-
| E || 11 || Nachdem die Doppelreihe B vollständig durchlaufen wurde, gibt es einen Übertrag in diese Reihe. Diese elf Mulden stehen dann für '''überzähligen Tage''' in einem Sonnenjahr (365,2 Tage) im Vergleich zu zwölf synodischen Monaten (354,4 Tage). Wenn diese Reihe bereits voll ist, gibt es für das nächste beginnende Jahr mit der nächstfolgenden '''Goldenen Zahl''' einen Übertrag in die Reihe A.
|-
| F || 24 + 1 = 25 || Für die 24 bis 25 '''vollendeten Mondviertel''' in der zweiten Hälfte eines Sonnenjahres, oder entweder für alle '''abnehmenden Mondviertel''' (wenn der Mond zu Beginn des Jahres zunehmend war) oder für alle '''zunehmenden Mondviertel''' (wenn der Mond zu Beginn des Jahres abnehmend war) eines Sonnenjahres (vergleiche Reihe D). Das 25. Loch ist etwas abgesetzt, da es für ein am Ende des Jahres eingeschaltetes 50. Mondviertel (Dauer = 7,38265 Tage) steht, das nur in ungefähr jedem zweiten Sonnenjahr auftritt:<br/>
:49 x 7,38265 Tage ≈ 361,75 Tage beziehungsweise 50 x 7,38265 Tage ≈ 369,13 Tage<br/>
:365,242 Tage - 361,75 Tage ≈ 3,5 Tage beziehungsweise 365,242 Tage - 369,13 Tage ≈ -3,9 Tage
|-
| G || 53 || Für die begonnenen 53 '''Siebentagewochen''' in einem Sonnenjahr (Dauer = 365,242 Tage) beziehungsweise von einem heliakischen Auf- oder akronychischen Untergang der Plejaden zum nächsten.
|}
[[Datei:Altlicht.16.9.2020.P1079087.jpg|mini|rechts|Über dem östlichen Horizont beim Morgenletzt gerade noch sichtbares Altlicht des abnehmenden Mondes 33 Stunden vor Neumond mit der vom Erdschein beleuchteten Nachtseite des Mondes. Die Aufnahme entstand kurz vor Herbstbeginn, als die Ekliptik morgens fast senkrecht auf dem Horizont stand.]]
Zu der Doppelreihe B sei angemerkt, dass auch im altägyptischen Mondkalender, der im Neolithikum in Verwendung war, der Monat nicht mit dem unsichtbaren Neumond, sondern mit dem gerade noch sichtbaren Altlicht des Morgenletztes des Mondes begann, also gut einen Tag vor Neumond.<ref>Joachim Friedrich Quack: [https://core.ac.uk/download/pdf/35120251.pdf Zwischen Sonne und Mond - Zeitrechnung im Alten Ägypten], Seite 38, in: Harry Falk (Herausgeber), ''Vom Herrscher zur Dynastie. Zum Wesen kontinuierlicher Zeitrechnung in Antike und Gegenwart'', Bremen 2002</ref> Die beiden letzten Löcher sind etwas nach links abgesetzt, was mit folgendem Sachverhalt im Einklang steht: zwei Tage vor dem Ende einer synodischen Periode, also schon nach gut 27 Tagen, ist ein siderischer Monat vorüber, nach welchem der Mond die gleiche ekliptikale Länge erreicht hat. Das heißt bereits nach gut 27 Tagen steht der Mond zum Beispiel wieder im Goldenen Tor der Ekliptik, bevor er erst nach gut 29 Tagen erneut sein Altlicht erreicht (gut einen Tag vor Neumond). Die Sonne ist innerhalb des synodischen Monats durch die Bewegung der Erde um die Sonne gegenüber dem Fixsternhimmel um knapp 30 Bogengrad weiter nach links gezogen.
Die 25 Wochen des abnehmenden Mondes werden zum Beispiel auch im 79. Kapitel des ungefähr 2000 Jahre alten {{w|Äthiopisches Henochbuch|äthiopischen Henochbuches}} erwähnt.<ref>Siehe auch {{s|Henochbuch_(oder_Erster_Henoch)#Der_dritte_Teil_72–82_Das_astronomische_Buch|Wikisource, Henochbuch, Teil 3, Kapitel 71 bis 82}}</ref> Alternativ könnten die 50 Löcher in Reihen D und F eventuell auch für die 50 vollständigen Siebentagewochen (350 Tage) innerhalb von zwölf synodischen Perioden stehen, die eine Dauer von 50,6 Wochen beziehungsweise 354,4 Tagen haben.
Zur Zahl Elf (Reihe E) ist noch festzuhalten, dass die Erde innerhalb eines siderischen Jahres des Planeten Jupiter (zwölf Erdenjahre) elf Mal mit diesem in Opposition steht. Zu diesen Zeitpunkten ist der Abstand zwischen Erde und Jupiter am geringsten, der Jupiter hat steht in seinem größten Glanz und er kulminiert um Mitternacht auf dem südlichen Meridian.
[[Datei:Mnajdra East Temple 2 (6946102161).jpg|rechts|mini|Detail des westlichen Kalendersteins aus dem Osttempel von Mnajdra.]]
Auch auf einem weiteren, sogenannten westlichen und heute ebenfalls aufgerichteten Stein der Tempelanlage sind mehrere Lochreihen zu sehen, die aus 16, 12, 19, 7, 30, 31, 32, 35, 37, 12 und 13 Löchern bestehen.<ref>David Humiston Kelley, Eugene Frank Milone: ''Exploring Ancient Skies: A Survey of Ancient and Cultural Astronomy'', Part II ''Astronomy in Cultures'', 6 ''Paleolithic and Neolithic Cultures'', 6.2 ''Megalithic Cultures'', 6.2.18 ''Mediterranean and North African Megalithic Sites'', 6.2.18.1 ''Malta'', pages 201 and 202, Springer, 2011, ISBN 9781441976246</ref> Einige dieser Zahlen tauchen auch im Zusammenhang mit dem östlichen Stein auf oder sind ebenfalls leicht mit lunaren oder solaren Kalendertagen in Verbindung zu bringen:
* 16: Anzahl der Tage vom Altlicht bis zum Vollmond.
* 12: Anzahl der vollständigen Monate pro Jahr.
* 19: Anzahl der tropischen Jahre pro Meton-Periode.
* 7: Anzahl der vollständigen Tage pro Mondviertel respektive der Tage pro Woche.
* 30: aufgerundete Anzahl der Tage pro synodische Periode respektive der Tage pro Monat.
→ In Bezug auf die Bedeutung von bestimmten Zahlen in der Astronomie siehe auch '''[[Quadriviale Kuriositäten/ Zahlen|Exkurs „Zahlen“]]'''.
<div style="clear:both"></div>
==Malereien in der Höhle von Magura==
In der schon im Neolithikum genutzten Magura-Höhle im Nordwesten des heutigen Bulgariens gibt es nicht nur eine sehr alte bildliche Darstellung eines Schöpfungsmythos, sondern ebenfalls Hinweise darauf, dass verschiedene Mondzyklen bekannt waren. Unter den Darstellungen befindet sich insbesondere eine Reihe von Strichen, mit denen die 16 Tage vom Altlicht des Mondes bis zu Vollmond gezählt worden sein können.
→ Weitere Erläuterungen finden sich im '''[[Die_Höhlenmalerei_in_der_Magura-Höhle|Wikibook „Die Höhlenmalerei in der Magura-Höhle“]]'''.
[[Datei:Magura.Altlicht.Vollmond.png|links|mini|hochkant=4|Erste Hälfte des synodischen Monats in einer Darstellung der Höhlenmalerei von Magura. Rechts ist die schmale, liegende Mondsichel des Altlichts beim Morgenletzt zu sehen. Ein bis zwei Tage später ist Neumond, danach nimmt der Mond wieder zu, und nach insgesamt sechzehn Tagen wird der Vollmond erreicht (links).]]
<div style="clear:both"></div>
== Kontrastverhältnisse bei aschgrauem Mondlicht ==
[[Datei:Aschgraues.Mondlicht.P1162513.jpg|mini|hochkant=2|Die vom Erdschein beleuchtete Nachtseite des Mondes wird neben der schmalen Mondsichel (11 Prozent, Mondalter = 3,3 Tage) mit ''aschgrauem Mondlicht'' sichtbar. Aufnahme über dem westlichen Abendhimmel kurz nach dem Äquinoktium im Frühjahr bei besonders steiler Ekliptik von Berlin aus gesehen. Links der Stern Omikron Arietis (37 Ari, 6,2<sup>m</sup>) im Sternbild Widder.<br/><br/>
Mit bloßen Augen ist das aschgraue Licht im Mondschatten nur unter sehr guten atmosphärischen Bedingungen und mit voller menschlicher Sehkraft gerade noch erkennbar. Der Mond hat nur einen Winkeldurchmesser von 0,5 Bogengrad, was bei ausgestrecktem Arm nur ungefähr einem Viertel der Breite eines Fingers entspricht. Die hier gezeigte Aufnahme ist sehr hell belichtet, so dass die Kontraste im Mondschatten einfacher wahrzunehmen sind als in der Natur:
[[Datei:Aschgraues.Mondlicht.P1162513.jpg|90px|zentriert]]
]]
Das '''aschgraue Mondlicht''' kann wenige Tage vor und nach Neumond im Schattenbereich des Mondes beobachtet werden, wenn die Sonne unter dem Horizont steht. Der Schattenbereich wird hierbei durch den '''Erdschein''' beleuchtet und hebt sich deutlich sichtbar vom Himmel ab, der den Mond umgibt. Der Erdschein hängt vom Albedo der vom direkten Sonnenlicht getroffenen Erde ab und kann insbesondere durch den Anteil an Wolken in der Atmosphäre sowie an Eis- und Schneeflächen auf der Erdoberfläche schwanken.
=== Historische Beobachtungen ===
Bereits die Babylonier haben im sechsten vorchristlichen Jahrhundert diesen visuellen Effekt in bildlichen Darstellungen auf Steinreliefs mit dem König Nabonid vor der Mondsichel mit aschgrauem Licht festgehalten:<ref>Friedhelm Pedde: ''[https://wfs.berlin/wp-content/uploads/2021/09/BROplanet-12-okt2021ff_www.pdf Götter und Planeten im Alten Orient - Sin und der Mond]'', Mitteilungen, Ausgabe 12, Seite 6 und 7, Oktober 2021, Wilhelm-Foerster-Sternwarte e.V. / Zeiss-Planetarium am Insulaner</ref>
<gallery caption="" mode="packed" widths="600" heights="400">
Urfa museum King Nabonid embossed inscription Iron age - 5026.jpg|Darstellung des letzten Königs des Neubabylonischen Reiches, Nabonid, vor der Mondsichel mit aschgrauem Mondlicht am Kopf einer babylonischen Stele aus Harran im Archäologischen Museum Şanlıurfa in der Türkei aus dem sechsten vorchristlichen Jahrhundert.
Nabonidus.jpg|Darstellung des letzten Königs des Neubabylonischen Reiches, Nabonid, vor der Mondsichel mit aschgrauem Mondlicht am Kopf einer babylonischen Stele im Britischen Museum.
Voynich.68r.Mondsichel.Erdschein.png|Darstellung der Mondsichel mit einem Gesicht im Schattenbereich des Mondes im astronomischen Abschnitt des Voynich-Manuskripts aus dem 15. Jahrhundert.
Caspar David Friedrich - Zwei Männer in Betrachtung des Mondes.jpg|Zwei Männer am Abend bei der Betrachtung des Neulichts von Caspar David Friedrich (1819/1820), Öl auf Leinwand, Albertinum, Dresden. Eine in dieser Darstellung ähnliche Konstellation zwischen einer solchen Mondsichel und dem Planeten Jupiter fand am Abend des 28. Märzes 1811 im Sternbild Stier (Taurus) statt. Eine Woche nach der Tag-und-Nacht-Gleiche des Frühjahrs stand der Mond wie im Bild dargestellt mit stark geneigter Sichel etwas tiefer als der Jupiter über dem westlichen Horizont.
Moon - Artemis II - April 6 2026 (55193552739).jpg|Die Aufnahme der Artemis-II-Besatzung während ihres Mondvorbeiflugs am 6. April 2026 zeigt eine Sonnenfinsternis. Am linken Rand der Mondscheibe ist das schwache Leuchten des aschgrauen Mondlichts zu erkennen, das vom Erdschein der links vom Mond befindlichen Erde hervorgerufen wurde.
</gallery>
=== Bestimmung der Kontraste ===
[[Datei:Altlicht.Mond.P1116624.jpg|mini|rechts|hochkant=2|Altlicht des Mondes am 3. November 2021 mit aschgrauem Licht im Schattenbereich des Mondes von Berlin aus gesehen.]]
Das nebenstehende Bild des Altlichts (Mondalter 27,7 von 29,5 Tagen) im Sternbild Jungfrau wurde am 3. November 2021 um 7:05 Uhr morgens mit einem hochwertigen Teleobjektiv mit guter Kontrastübertragung über dem ostsüdöstlichen Horizont (Azimut 122 Bogengrad) in Berlin aufgenommen. Die Sonne stand zum Zeitpunkt der Aufnahme noch gut ein Bogengrad unter dem Horizont, der Mond hatte eine nördliche ekliptikale Breite von gut 3 Bogengrad und eine Elongation von 22 Bogengrad, so dass er bei wolkenlosem Himmel und bei einer Höhe von 17,5 Bogengrad über dem Horizont gut zu beobachten war. Die Mondscheibe war mit bloßem Auge vollständig sichtbar und war zu 3,7 Prozent vom direkten Sonnenlicht beleuchtet.
Die aufgenommenen Rohdaten wurden für diese Auswertung mit einer Software mit möglichst neutralen Einstellungen zu einer Schwarz-Weiß-Aufnahme entwickelt (Kontrast = 1, Gamma = 1, Farbtemperatur = 4100 Kelvin, Farbsättigung = 0). Anhand der Bilddaten können die Luminanzwerte (Helligkeiten) im Bild bestimmt werden, um aus beliebigen paarweisen Luminanzwerten den jeweiligen Kontrast zu berechnen. Der '''Michelson-Kontrast''' <math>M</math> (auch '''Modulation'''), der sich aus zwei benachbarten Luminanzwerten <math>L_2</math> und <math>L_1</math> ergibt (Bedingung: <math>L_2 \ge L_1 \ge 0</math>), liegt immer zwischen 0 (<math>L_2 = L_1</math>, also kein Kontrast) und 1 beziehungsweise 100 Prozent (<math>L_1 = 0</math>, also maximaler Kontrast):
:<math>M = \frac {L_2 - L_1} {L_2 + L_1}</math>
→ Siehe auch [[Digitale_bildgebende_Verfahren:_Grundlagen#Modulation|Wikibook '''Digitale bildgebende Verfahren''', Kapitel '''Grundlagen''', Abschnitt '''Modulation''']].
Die Sichel ist in der ausgewerteten Aufnahme nicht überbelichtet, hat im Scheitel einen mittleren Luminanzwert von:
:<math>L_{Sichel} = 236</math>
Der den Mond umgebende Himmel hat durch das vorhandene Streulicht in der Troposphäre einen mittleren Luminanzwert von:
:<math>L_{Himmel} = 15</math>
Daraus resultiert ein entsprechend hoher Michelson-Kontrast <math>M</math>:
:<math>M_{Sichel,Himmel} = \frac {L_{Sichel} - L_{Himmel}} {L_{Sichel} + L_{Himmel}} = \frac {236 - 15} {236 + 15} = \frac {221} {251} = 0,88</math>
Im aschgrauen Licht in der Schattenseite des abnehmenden Mondes (dessen gesamte scheinbare Helligkeit beträgt bei der in der Abbildung sichtbaren Mondphase übrigens ungefähr -3<sup>m</sup>), die vom Erdschein beleuchtet wird, ergibt sich für die etwas variierende Reflektivität der Mondoberfläche ein mittlerer Luminanzwert von:
:<math>L_{Schatten} = 24,5</math>
Im Vergleich zur hellen Mondsichel ergibt sich daraus ein etwas geringerer Kontrast zwischen Mondsichel und Mondschatten als zwischen Mondsichel und Nachthimmel:
:<math>M_{Sichel,Schatten} = \frac {L_{Sichel} - L_{Schatten}} {L_{Sichel} + L_{Schatten}} = \frac {236 - 24,5} {236 + 24,5} = \frac {211,5} {260,5} = 0,81</math>
Die Standardabweichung beträgt in der Schattenseite des abnehmenden Mondes:
:<math>L_{Abweichung} = 2,5</math>.
Innerhalb der sichtbaren Schattenseite des Mondes resultiert aus den dort mit einer Standardabweichung um den Mittelwert herum auftretenden Luminanzwerten für das aschgraue Licht ein Kontrast von nur 10 Prozent:
:<math>M_{Schatten} = \frac {(L_{Schatten} + L_{Abweichung}) - (L_{Schatten} - L_{Abweichung})} {(L_{Schatten} + L_{Abweichung}) + (L_{Schatten} - L_{Abweichung})} = \frac {L_{Abweichung}} {L_{Schatten}}= \frac {2,5} {24,5} = 0,10</math>
Dieser Kontrastwert ergibt sich im Übrigen im gleichen Maße auch auf der von der Sonne voll beleuchteten und dann jedoch insgesamt erheblich helleren Oberfläche des Vollmonds (die scheinbare Helligkeit beträgt dann ungefähr -13<sup>m</sup>).
:'''Anmerkung''': ''Die Sinnhaftigkeit der Kontrastbestimmung mit der Standardabweichung begründet sich in der Tatsache, dass eine perfekte schwarz-weiße Kante mit einem maximalen Luminanzwert <math>L_{max}</math> im hellen Bereich und einem Luminanzwert von Null im dunklen Bereich eine Modulation <math>M</math> von eins hat, sofern die weiße und die schwarze Teilfläche gleich groß sind und jeweils <math>\frac {n} {2}</math> Messwerte beinhalten. Sowohl der Mittelwert aller <math>n</math> Luminanzwerte als auch die Standardabweichung aller <math>n</math> Luminanzwerte sind dann beide genau halb so groß wie der maximale Luminanzwert:''
::<math>L_{Mittelwert} = \sum_{i=1}^n L_{i} = \sum_{i=1}^{\frac {n} {2}} 0 + \sum_{i=1}^{\frac {n} {2}} L_{max} = \frac {L_{max}} {2}</math>
::<math>L_{Standardabweichung} = \sqrt {\frac {\sum_{i=1}^n (L_{i} - L_{Mittelwert})^2} {n}} = \sqrt {\frac {\sum_{i=1}^n \left( \frac {L_{max}} {2} \right)^2} {n}} = \sqrt {\frac {n \cdot \left( \frac {L_{max}} {2} \right)^2} {n}} = \frac {L_{max}} {2}</math>
:''Der Mittelwert zuzüglich der Standardabweichung entspricht dann dem maximalen Luminanzwert:''
::<math>L_{Mittelwert} + L_{Standardabweichung} = \frac {L_{max}} {2} + \frac {L_{max}} {2} = L_{max}</math>
:''Und der Mittelwert abzüglich der Standardabweichung hat dann den Wert Null:''
::<math>L_{Mittelwert} - L_{Standardabweichung} = \frac {L_{max}} {2} - \frac {L_{max}} {2} = 0</math>
:''Der auf diesem Weg bestimmte Kontrast stimmt mit dem oben genannten Kontrast an der Kante überein:''
::<math>M = \frac {L_{max} - 0} {L_{max} + 0} = \frac {L_{max}} {L_{max}} = 1</math>
Schließlich kann auch noch der Kontrast an der Außenkante der nur vom Erdschein beleuchteten Mondscheibe bestimmt werden, also zwischen aschgrauem Mondlicht und Himmel:
:<math>M_{Schatten,Himmel} = \frac {L_{Schatten} - L_{Himmel}} {L_{Schatten} + L_{Himmel}} = \frac {24,5 - 15} {24,5 + 15} = \frac {9,5} {39,5} = 0,24</math>
Dieser Kontrast ist also deutlich höher als der innerhalb des Mondschattenbereichs. Die Helligkeit wechselt an der Kante der Mondscheibe zudem schlagartig und ist deswegen besser wahrnehmbar als die Helligkeitsunterschiede innerhalb des Schattenbereiches, wie im Folgenden ausgeführt wird.
=== Wahrnehmungsphysiologie ===
[[Datei:CSF.against.angular.frequency.in.cycles.per.degree.png|mini|rechts|hochkant=2|Kontrastempfindlichkeitsfunktion (englisch: Contrast Sensitivity Function, CSF) des menschlichen Auges über der Ortsfrequenz <math>f_a</math> in Linienpaaren pro Grad.]]
Strukturen, bei denen sich die Helligkeit innerhalb eines achtel Bogengrades (7,5 Bogenminuten) ändert, werden von einem gesunden menschlichen Auge nahezu perfekt auf die Netzhaut übertragen und sind deshalb am besten wahrzunehmen. Weichere Strukturen mit sich langsamer und kontinuierlich ändernder Helligkeit oder kleinere Strukturen sind bei gleichem Objektkontrast schwieriger zu erkennen. Ob ein bestimmter Objektkontrast mit bloßem Auge wahrgenommen werden kann, kann mit Hilfe der Kontrastempfindlichkeitsfunktion (Contrast Sensitivity Function (CSF)) des menschlichen Auges ermittelt werden, bei der die Kontrastübertragung von einem Objekt auf die Netzhaut des Auges über der Ortsfrequenz in Linienpaaren pro Bogengrad angegeben ist:
→ Siehe auch [[Digitale_bildgebende_Verfahren:_Grundlagen#Kontrastempfindlichkeitsfunktion|Wikibook '''Digitale bildgebende Verfahren''', Kapitel '''Grundlagen''', Abschnitt '''Kontrastempfindlichkeitsfunktion''']].
[[Datei:CSF.UHD.png|mini|rechts|hochkant=2|Zur Kontrastempfindlichkeitsfunktion des menschlichen Auges mit den Ortsfrequenzen von 2 sinusmodulierten Linienpaaren pro Bildbreite (linke Bildhälfte) bis hin zu 1024 Linienpaaren pro Bildbreite (rechts am Bildrand), Bildgröße 3860 mal 2160 Bildpunkte (16:9) mit maximalem Kontrast unten, ohne Kontrast oben.]]
Das nebenstehende Diagramm und die darunter befindliche unfarbige Graphik mit den sinusförmigen Helligkeitsmodulationen visualisieren dies. Von links nach rechts wächst die Ortsfrequenz ausgehend von zwei Linienpaaren pro Bildbreite immer weiter, und der Kontrast ändert sich in vertikaler Richtung kontinuierlich zwischen den Extremwerten 0 und 1. Bei großen und sehr kleinen Ortsfrequenzen sind geringe Kontraste nicht so gut zu erkennen wie im mittleren Bereich. Die maximale Empfindlichkeit wird bei acht Linienpaaren pro Bogengrad erreicht und hängt bei einem gegebenen Objekt demzufolge vom Betrachtungsabstand ab.
Das Auge kann nach dieser Kontrastempfindlichkeitsfunktion innerhalb eines Bogengrades maximal bis zu 80 Linien mit maximalem Objektkontrast (also 40 Linienpaare mit dem Kontrast 1) wahrnehmen. In diesem Fall liegt der wahrgenommene Kontrast zwischen den benachbarten Linien bei ungefähr 5 Prozent. Bei 120 Linien mit maximalem Objektkontrast (also 60 Linienpaare mit dem Kontrast 1) sinkt der wahrgenommene Kontrast auf unter ein Prozent und ist somit praktisch nicht mehr wahrnehmbar. Wegen des scheinbaren Winkeldurchmessers der Mondscheibe von 0,5 Bogengrad, kann das Auge innerhalb des Monddurchmessers also maximal 20 Linienpaare beziehungsweise 40 Linien unterscheiden. Insbesondere an der scharfen Kante der Mondscheibe ist eine ins Auge nahezu verlustfrei übertragene Ortfrequenz von acht Linienpaaren pro Bogengrad stark vertreten, so dass der in der obigen Aufnahme des Mondes vorhandene Objektkontrast von 24 Prozent zwischen Mondschatten und Himmel (siehe oben) gut wahrgenommen werden kann.
Etwas schwieriger gestaltet sich dies innerhalb des Schattenbereichs des Mondes. In der obigen Aufnahme tauchen die stärksten Kontraste entlang einer Linie von oben nach unten auf, wo die helleren Mondebenen mit den Kratern Copernicus und Tycho durch die dunkleren Maria Imbrium und Nubium unterbrochen werden. Daraus resultiert eine Ortsfrequenz mit fünf Linienpaaren pro Bogengrad, wo die Kontrastempfindlichkeitsfunktion ebenfalls einen recht hohen Wert von 0,9 beziehungsweise 90 Prozent erreicht. Dies schwächt den auf der Mondoberfläche vorhandenen Objektkontrast von 10 Prozent im vom Auge auf die Netzhaut übertragenen Bildkontrast nur geringfügig ab.
Aus diesen Überlegungen resultiert, dass die im aschgrauen Licht des Mondes wahrnehmbaren Kontraste knapp 10 Prozent betragen. Diese sind zwar geringer als der Kontrast von 24 Prozent zwischen dem aschgrauen Mondlicht und dem den Mond umgebenden Himmel, sie liegen allerdings noch in einem wahrnehmbaren Bereich. Somit ist erwiesen, dass ein gesundes Auge unter idealen Beobachtungsbedingungen auch völlig ohne optische Hilfsmittel die Helligkeitsunterschiede und Strukturen im nur durch den Erdschein erhellten Schattenbereich des Mondes erkennen kann.
Es gibt jedoch zahlreiche Faktoren, die den wahrnehmbaren Kontrast deutlich herabsetzen oder die Möglichkeit der Wahrnehmung verhindern können. Je älter ein Mensch wird, desto mehr Streulicht wird durch kleinste Verletzungen beziehungsweise Narben in der Hornhaut oder in der Linse des Auges erzeugt. Dieses Streulicht erzeugt einen mehr oder weniger gleichmäßigen Lichtschleier, der auch die dunklen Stellen auf der Netzhaut beleuchtet. Dies führt in jedem Fall zu einer Herabsetzung des wahrnehmbaren Kontrastes und verhindert somit das Erkennen von schwachen Kontrasten.
=== Begrenzende Einflüsse ===
Aber auch in der Natur gibt es viele begrenzende Faktoren. Hierzu gehört das natürliche Streulicht durch die Sonne in der Troposphäre, wenn diese zu dicht am Mond oder zu dicht unter dem Horizont oder gar über dem Horizont steht. In bewohnten Gegenden kommt als großer Störfaktor noch die Lichtverschmutzung hinzu. Ferner wird der Objektkontrast durch atmosphärische Störungen vermindert. Diese ergeben sich zum einen durch Dunst, Nebel oder Staub in der Atmosphäre, und zum anderen aus den langen Wegstrecken, die das Licht bei einer Beobachtung eines Himmelsobjekts durch die Troposphäre zurücklegen muss, wenn dieses sehr dicht über dem Horizont steht. Hierbei wird das den Beobachter erreichende Licht nicht nur abgeschwächt ('''Extinktion'''), sondern auch durch das Licht aller möglichen anderen Lichtquellen angereichert, das an den Luftmolekülen gestreut oder durch Mikroturbulenzen abgelenkt wurde, so dass sich die minimal vorhandene Helligkeit vergrößert und der Kontrast somit verringert. In bewohnten Regionen ist zudem mit erheblicher Lichtverschmutzung durch künstliche Lichtquellen zu rechnen.
Auf der anderen Seite wird insbesondere in Horizontnähe der blaue Lichtanteil von astronomischen Objekten auf dem Lichtweg durch die Troposphäre durch die stark wellenlängenabhängige '''Rayleigh-Streuung''' seitlich abgelenkt, so dass es praktisch gar nicht mehr zum Beobachter gelangt. Dies führt zu den rötlichen Auf- und Untergängen von Sonne und Mond sowie zur Blaufärbung des Taghimmels.
Unter Umständen muss das Licht mehrere hundert Kilometer durch die Atmosphäre zurücklegen, wie in der folgenden Abbildung zu erkennen ist:
[[Datei:Troposphaerische.Laengen.png|mini|zentriert|hochkant=4|Weglängen für Lichtstrahlen in der 15 Kilometer hohen Troposphäre in Abhängigkeit von der Zenitdistanz.]]
=== Beobachtung ===
Die Beobachtungsmöglichkeiten des aschgrauen Lichts werden grundsätzlich durch die folgenden, sich teilweise widerstrebenden Bedingungen verbessert:
* Die '''Lichtverschmutzung''' in der Atmosphäre ist gering.
* Das '''Mondalter''' beträgt wenige Tage vor oder nach Neumond.
* Die '''Sonne''' steht möglichst weit unter dem Horizont.
* Der '''Mond''' hat eine möglichst große Höhe über dem Horizont.
* Die '''Mondsichel''' ist möglichst klein, um möglichst wenig Streulicht zu verursachen.
Für die Beobachtung aus nördlichen geographischen Breiten gilt insbesondere:
* Der Mond hat eine möglichst große '''nördliche ekliptikale Breite''' zwischen seinem aufsteigenden und seinem absteigenden Knoten.
* Die '''Ekliptik''' steht möglichst steil auf dem Horizont. Dies ist bei den Tag-und-Nacht-Gleichen der Fall, besonders gut
** abends im Frühjahr beim '''Neulicht''' des Mondes nach Sonnenuntergang im Westen.
** morgens im Herbst beim '''Altlicht''' des Mondes vor Sonnenaufgang im Osten.
=== Farbigkeit ===
[[Datei:Neulicht.21.3.2026.HDR.1024.png|mini|hochkant=2|Die Abbildung zeigt eine Hochkontrastaufnahme des Erdscheins im Mondschatten zum Frühlingbeginn drei Tage nach Neumond mit Sternen bis zur zehnten Größenklasse (10<sup>m</sup>) in der Umgebung. Die Sichel zeigte einen Tag nach der Tag-und-Nacht-Gleiche im Frühjahr 2026 und zwei Tage vor der großen Mondwende bei einer nördlichen ekliptikalen Breite des Mondes von vier Bogengrad eine besonders starke Neigung. Die Sonne war zuvor im Westen untergegangen und stand bereits elf Bogengrad unter dem Horizont. Die Symmetrieachse der Mondsichel (-7<sup>m</sup>) auf der zu 9,3 Prozent beleuchteten Mondscheibe zeigt nach rechts unten in Richtung der Sonne (Farbtemperatur = 4100 Kelvin, Belichtungsreihe mit sieben Bildern in ganzen Blendenstufen).]]
Bei genauer Betrachtung und Beobachtung ist das sogenannte aschgraue Licht gar nicht farbneutral, sondern spiegelt einen mittleren Farbton der von der Sonne beleuchteten Erdoberfläche wider, von der wiederum der Mondschatten beleuchtet wird. Je niedriger der Bewölkungsgrad, desto stärker ist zudem der Einfluss der farbigen Oberfläche der Erde. Die Farben der Erdoberfläche schwanken hierbei deutlich zwischen den Ozeanen, Wäldern oder Wüsten. In der nebenstehenden Hochkontrastaufnahme zeigt der Mondschatten im HSV-Farbraum einen mittleren Farbwert mit dem Farbwinkel 202°, was als Farbton blaucyan übersetzt werden kann. Dies entspricht recht gut dem mittleren Farbton der der Sonne zugewandten Seite der Erde ("Blue Marble") während der Aufnahme, der für das hier gezeigte Beispielbild im HSV-Farbraum einen mittleren Farbwert mit dem Farbwinkel 201° hat. Der Farbwinkel ist von der Sättigung oder der Helligkeit nicht abhängig.
[[Datei:Neulicht.21.3.2026.HDR.Original.Durchschnitt.gesaettigt.aufgehellt.png|mini|links|hochkant=4|Sichtbarmachung des Farbwerts mit dem Farbwinkel 202° des aschgrauen Mondlichts in einer Hochkontrastaufnahme des Erdscheins im Mondschatten zum Frühlingbeginn drei Tage nach Neumond.<br/>Von links nach rechts:<br/>1. Originalaufnahme<br/>2. Mittelwert<br/>3. Farbe gesättigt<br/>4. aufgehellt]]
[[Datei:North America from low orbiting satellite Suomi NPP.Original.Durchschnitt.gesaettigt.abgedunkelt.png|mini|links|hochkant=4|Sichtbarmachung des Farbwerts mit dem Farbwinkel 201° der Erdoberfläche im Bereich von Nordamerika und dem südöstlichen Pazifik.<br/>Von links nach rechts:<br/>1. Originalaufnahme<br/>2. Mittelwert<br/>3. Farbe gesättigt<br/>4. abgedunkelt]]
<div style="clear:both"></div>
==Einzelnachweise==
<references></references>
<noinclude>
[[Kategorie:Astronomische Kuriositäten]]
</noinclude>
mjvao3m19jagcfc2nsioy0auxlkjw0n
1092186
1092185
2026-10-17T11:30:23Z
Bautsch
35687
/* Siderischer Monat */ Sonne, Mond, Jupiter / Manazil al-Qamar
1092186
wikitext
text/x-wiki
[[Datei:Mondfinsternis.7.9.2025.20.46MESZ.Berlin.png|mini|rechts|hochkant=2|Ein bei Vollmond während einer Mondfinsternis im Kernschatten der Erde befindlicher Blutmond nach Mondaufgang von Berlin aus gesehen am 7. September 2025.]]
== Monat ==
Die Bezeichnung '''Monat''' stammt etymologisch von unserem Erdmond ab. Es handelt sich um ein Erbwort, das auf die seit dem 8. Jahrhundert bezeugten althochdeutschen Formen ''mānōd'' beziehungsweise ''mānōth'' zurückgeht. Diese wiederum stammen vom indoeuropäischen Wort ''mēnōt'' ab, das sowohl ''Monat'' als auch ''Mond'' bedeuten kann.<ref>[https://indogermanisch.org/pokorny-etymologisches-woerterbuch/m%C4%93n%C5%8Dt_gen_m%C4%93neses_woraus_m%C4%93nes-_m%C4%93ns-_m%C4%93s-_m%C4%93n.htm mēnōt], Pokorny - Indogermanisches etymologisches Wörterbuch</ref>
== Die Naturgeschichte von Plinius dem Älteren ==
'''Gaius Plinius Secundus''' ("der Ältere") hat im ersten nachchristlichen Jahrhundert über den '''Mond''' geschrieben. Das neunte Kapitel des zweiten Buches aus dem ersten Band hat den Titel ''Von der Natur des Mondes''. Über den Mond schreibt Plinius Folgendes:<ref name="Denso1764">Plinius: ''Naturgeschichte'', erster Band, zweites Buch, neuntes Kapitel: ''Von der Natur des Mondes'', übersetzt von Johann Daniel Denso (1708–1795), Rostock und Greifswald, Anton Ferdinand Rösens Buchhandlung, 1764, in moderne deutsche Sprache gesetzt.</ref>
<blockquote>
Aber alle diese [Wandelgestirne] übertrifft an Bewunderungswürdigkeit das Gestirn des '''Mondes''', das niedrigste, der Erde bekannteste, und zur Verminderung der Finsternis erfunden. Dieser hat durch seinen Wandel und veränderte Gestalt, da er beständig zu- und abnimmt, den Witz seiner Beobachter gequält, die es sich für einen Schimpf hielten, wenn man das nächste Gestirn nicht kennen sollte. Ja er ist auch in Spitzen gekrümmt, bald gerade halbgeteilt, bald in eine ganze Scheibe gebogen, bald, da er einem dunkeln Flecken ähnlich war, scheint er plötzlich hervor, und ist ist er ohne Grenzen rund und voll, ist gar nicht zu sehen. Bald scheint er die ganze Nacht durch, bald geht er spåt auf, und verstärkt noch bei Tage den Schein der Sonne. Er wird verfinstert, und lässt sich auch in der Verfinsterung noch sehen. Beim Ausgang des Monats sieht man ihn nicht, und man glaubt nicht, dass er alsdenn verfinstert werde.<br/>
Bald steht er niedrig, bald erhaben, und auch dies nicht auf eine Art. Bald steht er in der Höhe des Himmels, bald reicht er an die Berge: jetzt steht er hoch gegen Norden, bald tief gegen Süden herunter. Alles dies hat '''Endymion''' unter allen Menschen an ihm zuerst wahrgenommen, und deswegen ist die Sage, dass dieser in ihm verliebt gewesen sei. Wir sind wahrlich gegen diejenigen, welche uns zuerst bei diesem Himmelslicht durch ihre Arbeit und Sorge ein Licht aufgesteckt haben, nicht dankbar. Es ist ein wunderbares Verderben des menschlichen Witzes, dass wir ein Vergnügen daran finden, Blutvergießen und Mord in unsern Jahrbüchern aufzuzeichnen, damit die, welche die Welt nicht kennen, doch von unsern Lastern wissen mochten.<br/>
Doch aber da der Mond der Weltachse am nächsten steht, und also den engsten Umlauf hat, so durchlauft er in 27 und 1/3 Tag eben den Raum, welchen der höchste Stern des Saturns, wie gesagt ist, in dreißig Jahren zurücklegt. Hiernächst hält er sich in der Zusammenkunft mit der Sonne zwei Tage, wenn es am langsamsten geschieht, aus, und geht mit dem dreißigsten Tag zu seinen vorigen Abwechselungen wieder hervor. Ich weiß nicht, ob er nicht unter allen am Himmel der einzige Lehrmeister gewesen sei, dass man das Jahr in einen Raum von zwölf Monaten einteilen müsse, denn ebenso oft folgt er der Sonne, wenn sie zu einem neuen Zeichen zurückkehrt.<br/>
Er wird, wie die übrigen Sterne vom Sonnenlicht verdeckt. Denn wir sehen, dass er von derselben sein Licht gänzlich entlehne, und damit dergestalt leuchte, als wenn wir ihr zurückprallendes Licht in einem Wasser schweben sehen. Da er die Feuchtigkeit mit sanfterer und unvollkommener Kraft auflöst, auch wohl vermehrt, welche die Strahlen der Sonne verzehren. Hierher kommt es auch, dass man ihn in ungleichem Licht sieht, dass er alsdenn nur voll ist, wenn er der Sonne gerade gegenübersteht, in den übrigen Tagen nur so viel desselben, als er von jener empfängt, auf seiner Scheibe zeigt. Wenn er mit der Sonne zusammenkommt, kann man ihn nicht sehen, weil er uns die dunkle Seite zukehrt, und alles Geschöpfte dorthin, woher er es empfangen hat, wieder zurückwirft.<br/>
Es scheint außer Zweifel gesetzt zu sein, dass die Gestirne ihre Nahrung von der Feuchtigkeit der Erde haben. Denn er wird auf der einen Hälfte seiner Scheibe oft voller Flecken gesehen, weil er etwa noch nicht Kraft genug hat, mehr als ihm dienlich ist, an sich zu ziehen. Seine Flecken sind gewiss nichts anderes, als die mit der Feuchtigkeit von der Erde mit fortgerissenen Unreinigkeiten.<br/>
</blockquote>
<div style="clear:both"></div>
== Mondperioden ==
Die zu beobachtende scheinbare Mondbahn kann im Verlauf verschiedener Perioden durch zahlreiche '''Mondzyklen''' beschrieben werden. Die kürzesten Zyklen dauern ungefähr einen Monat im Sonnenkalender, sie längeren Mondzyklen können aber auch mehrere Jahre umfassen.
Der Mond hat ähnlich wie die Sonne einen scheinbaren Winkeldurchmesser von ungefähr 30 Bogenminuten beziehungsweise 0,5 Bogengrad. Dies entspricht bei Betrachtung des eigenen Fingers mit ausgestrecktem Arm in etwa einem Viertel der Fingerdicke.
===Synodischer Monat===
[[Datei:Courtenay.Mondsichel.P1033938.png|mini|Altlicht beim Morgenletzt des Mondes (heliakischer Aufgang) über dem östlichen Horizont auf Vancouver Island kurz nach der Tag-und-Nachtgleiche im September 2019.]]
[[Datei:Neulicht.37Tauri.P1138734.jpg|mini|Neulicht beim Abenderst des Mondes (akronychischer Untergang) über dem westlichen Horizont in Berlin Anfang Mai 2022.]]
Der synodische Monat ist durch den Verlauf der Elongation des Mondes in Bezug zur Sonne beschrieben.
Der Mond umrundet die Erde ungefähr zwölfmal schneller als die Erde die Sonne und benötigt für einen Umlauf einen Monat. Die einfachste Wahrnehmung des Mondlaufs ergibt sich durch die Beobachtung der Mondphasen beziehungsweise der Elongationen des Mondes. Der '''synodische Monat''' (altgriechisch ''σύνοδος'' (''synodos'') = ''Zusammentreffen'') beschreibt die Dauer zwischen zwei gleichen Mondphasen, also von Neumond zu Neumond beziehungsweise von Vollmond zu Vollmond. Hier wird gemeinhin das Zusammentreffen von Neumond und Sonne am Himmel als Referenzzeitpunkt betrachtet. Ein synodischer Monat dauert etwa 29,53 Tage, und zwölf synodische Monate dauern demzufolge rund 354,37 Tage - das sind gut fünfeinhalb Tage weniger als 360. Dieser Zyklus ist dies Basis für die gängigen Mondkalender (Lunarkalender) mit der gegenüber dem am Sonnenjahr orientierten Solarkalender um zirka 11 Tagen kürzeren Jahreslänge. Bei Lunisolarkalendern wird durchschnittlich alle drei Jahre ein dreizehnter synodischer Monat eingeschaltet, damit der Frühlingspunkt der Sonne ungefähr in der gleichen Jahreszeit bleibt.
→ Zur Zahl Zwölf siehe auch [[Quadriviale Kuriositäten/ Zahlen#Zur Zwölf|Exkurs '''Zur Zwölf''']].
[[Datei:Horusauge.Faltung.png|mini|rechts|hochkant=2|Das altägyptische Horusauge als Folge von Rechtecken mit jeweils der Hälfte der Fläche des Vorgängers in einem Rechteck mit dem Flächeninhalt eins.]]
[[Datei:Dendera_Deckenrelief_03.JPG|mini|hochkant=2|Deckenrelief im altägyptischen Tempel von Dendera mit der Darstellung von 15 Mondphasen von links (Neumond) nach rechts (Vollmond) mit den Göttern Junit, Sopdet-Tjenenet, Hor-Behdeti, Hathor, Nephthys, Harsiese, Isis, Osiris, Nut, Geb, Tefnut, Schu, Atum und Month. Im Vollmond vor dem Gott des Mondes Thot ist das von ihm geheilte linke Auge („Mondauge“) des Lichtgottes Horus dargestellt.]]
Die Ägypter haben ihre Kalender nicht mit einer Siebentagewoche, sondern sowohl mit einer Fünftagewoche als auch mit einer Zehntagewoche geführt. Damit besteht ein synodischer Monat auf weniger als einen halben Tag genau aus sechs Fünftagewochen beziehungsweise aus drei Zehntagewochen. Es wird in der Literatur manchmal darauf hingewiesen, dass das Verhältnis der Länge eines synodischen Monats zu dreißig vollen Tagen
:<math>\frac {29,530589 \text{d}} {30 \text{d}} = 0,984353</math>
fast identisch mit dem folgenden Verhältnis ist (siehe auch Horusauge und Heqat in der altägyptischen Geschichte<ref>Donald Frazer: ''Hieroglyphs and Arithmetic of the Ancient Egyptian Scribes'', Kapitel 2.6.5 ''Hekat Fractions and Ro'', Xlibris Corporation, 2012, ISBN 9781469136462</ref>):
:<math>\frac {1} {2} + \frac {1} {4} + \frac {1} {8} + \frac {1} {16} + \frac {1} {32} + \frac {1} {64}</math>
:<math>= \frac {32} {64} + \frac {16} {64} + \frac {8} {64} + \frac {4} {64} + \frac {2} {64} + \frac {1} {64}</math>
:<math>= \frac {63} {64} = \frac {2^6 - 1} {2^6} = 1 - 2^{-6} = 0,984375</math>
Die Abweichung der beiden Verhältnisse beträgt nur 0,022 Promille. Erst nach rund 44700 Monaten oder 3700 Jahren hat sich diese Abweichung auf einen Tag aufsummiert.
Die verschiedenen Mondphasen waren für die Menschen schon immer sichtbar und konnten im Laufe eines synodischen Monats verfolgt werden. Es wird davon ausgegangen, dass zum Beispiel auch auf der Himmelsscheibe von Nebra mindestens eine Mondsichel dargestellt ist, eventuell auch der Vollmond und nach dem österreichischen Ur- und Frühgeschichtler '''Paul Gleirscher''' zusätzlich das Altlicht des Mondes:<ref>Paul Gleirscher: [https://dialnet.unirioja.es/servlet/articulo?codigo=4142096 Zum Bildprogramm der Himmelsscheibe von Nebra: Schiff oder Sichel?], Germania: Anzeiger der Römisch-Germanischen Kommission des Deutschen Archäologischen Instituts, Band 85, Nummer 1, ISSN 0016-8874, Seiten 23 bis 33, 2007</ref>
<gallery caption="Verschiedene möglicherweise auf der Himmelsscheibe von Nebra dargestellte Mondphasen" perrow="2" widths="300" heights="300">
Vollmond.P1080516.jpg|Ein im Dezember um Mitternacht fast im Zenit stehender, sehr heller Vollmond.
Zunehmende.Mondsichel.png|Ein zunehmender Mond drei Tage nach Neumond beim akronychischen Untergang am westlichen Abendhimmel drei Wochen vor der Tag-und-Nacht-Gleiche im Herbst. Die rötliche Färbung entstand genauso wie bei der untergehenden Sonne durch die Rayleigh-Streuung in der Erdatmosphäre.
Altlicht.3.Nov.2021.P1116624.jpg|Das Altlicht eines abnehmenden Mondes (Morgenletzt, vier Prozent beleuchtet) beim heliakischen Aufgang am südöstlichen Morgenhimmel der nördlichen Hemisphäre während der bürgerlichen Dämmerung einen Monat nach der Tag-und-Nacht-Gleiche im Herbst mit Erdschein.
Nebra_Scheibe_Modell.jpg|Vollständig rekonstruiertes Modell der bronzenen und mit Gold tauschierten Himmelsscheibe von Nebra.
</gallery>
===Siderischer Monat===
Der '''siderische Monat''' ist durch den Verlauf der ekliptikalen Länge des Mondes in Bezug zum Frühlingspunkt der Sonnenbahn einer astronomischen Epoche definiert und beschreibt die Zeitspanne, nach der der Mond bei seinem Umlauf um die Erde Richtung Osten erneut die ekliptikale Länge des Frühlingspunktes erreicht hat. Da sich er Frühlingspunkt gegenüber dem Fixsternhimmel entlang der Ekliptiklinie verschiebt, ist der siderische Monat vom '''tropischen Monat''' zu unterscheiden. Letzterer bezieht sich ebenfalls auf den Frühlingspunkt, allerdings den des aktuellen Datums und nicht den, der zu einem bereits vergangenen und unveränderlichen Referenzzeitpunkt des Datums einer bestimmten astronomischen Epoche gegolten hat. Zur Zeit wird hierfür meist die Julianische Standardepoche J2000.0 verwendet, die sich auf die Uhrzeit 11:58:55,8 UTC am 1. Januar 2000 bezieht. Bezogen auf diese Standardepoche dauert ein tropischer Monat 27,32158 Tage. Durch die langsame gegenläufige Wanderung des Frühlingspunkts Richtung Westen besteht ein kleiner Unterschied zwischen tropischem und siderischem Monat, der mit 27,32166 Tage geringfügig länger ist. Die Differenz von 0,00008 Tagen entspricht einer Abweichung von von lediglich sieben Sekunden pro Monat: Dies bedeutet, dass der Mond den Frühlingspunkt des aktuellen Datums nach einer Umrundung (griechisch ''tropos'' = ''Drehung'') der Erde sieben Sekunden früher erreicht als einen Stern (lateinisch ''sideris'' = ''des Sterns'') der sich am Anfang der Umrundung noch beim Frühlingspunkt des einen Monat alten Datums befunden hat.
Es kann auch die Zeitspanne betrachtet werden, in der der Mond in Bezug auf den Fixsternhimmel entlang der Ekliptik wieder an einer beliebigen gleichen Stelle erscheint. Dies ist also beispielsweise auch die Dauer zwischen zwei aufeinanderfolgenden Erscheinungen des Mondes im '''Goldenen Tor der Ekliptik''', da dessen Lage durch die Sterne des Fixsternhimmels bei einer bestimmten ekliptikalen Länge bestimmt ist.
→ Siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Das_Goldene_Tor_der_Ekliptik|Exkurs '''Das Goldene Tor der Ekliptik''']].
Die Einteilung der 360 Bogengrad langen Ekliptik in 28 gleiche Teile ist in der Bronzezeit verbreitet gewesen. Daraus ergibt sich ein grobes Koordinatenraster für die ekliptikale Länge des Mondes mit 28 Mondhäusern respektive Mondstationen.
Auf der '''Stachelscheibe von Platt''' aus der Bronzezeit (um 1500 vor Christus) werden die 28 Mondorte der Tage eines Monats beispielsweise durch eine Kreisreihe dargestellt. Die entsprechende Hohlform hat insgesamt sieben konzentrische Kreise und diente zum Gießen von Schmuckscheiben. Zwei der Kreise bestehen zwei aus 12 (innen) beziehungsweise aus 28 (außen) gleichmäßig verteilten Mulden.<ref>Irene Hager und Stefan Borovits (Wien, Österreich): ''Der Vorläufer einer Oktaëteris auf dem Kalenderstein bei Leodagger/Pulkau?'', Kapitel 26.2.2 ''Astronomisch/kalendarische "Zählmaschinen" aus der Bronzezeit'', in: Gudrun Wolfschmidt (Herausgeberin): ''Orientierung, Navigation und Zeitbestimmung - Wie der Himmel den Lebensraum des Menschen prägt'', Proceedings der Tagung der Gesellschaft für Archäoastronomie in Hamburg 2017, Band 42 von Nuncius Hamburgensis - Beiträge zur Geschichte der Naturwissenschaften, Verlag tredition, 2019, ISBN 9783749767717</ref> Die zwölf inneren Mulden entsprechen entlang der Ekliptik den 12 Sonnenorten (Monaten) in einem tropischen Jahr beziehungsweise den 12 Jupiterorten (Jahren) in zwölf Jahren. Die 28 äußeren Mulden entsprechen entlang der Ekliptik den 28 Mondorten und somit den Tagen in einem siderischen Monat. Der große kreisförmige Stachel im Zentrum der Scheibe könnte als Symbol für die Sonne stehen. Auf ihm konnte die Scheibe von unten zentrisch und drehbar gelagert werden. Mit der Scheibe konnte (abgesehen vom ungefähr alle drei Jahre zusätzlich erforderlichen 14. siderische Schaltmonat) zwölf Jahre lang in täglich wechselnden Kombinationen in den beiden Lochreihen die Lagen von Mond und Jupiter eingestellt und abgelesen werden. Damit konnte nach einer Einmessung der Ost-West-Richtung zum Beispiel bei der Tag-und-Nacht-Gleiche zur Jupiterrichtung im Frühlingspunkt der gesamte Lebewesenkreis (Zodiak) jederzeit mit dem täglich ein Mondhaus weiterwandernden Mond vollständig bestimmt werden, auch ohne dass der Jupiter immer sichtbar sein musste. Mit dieser Information ist es dann ohne weiteres möglich, mit Hilfe des Monds oder des Jupiters die aktuelle Richtung der Sonne oder mit Hilfe der Sonne, des Monds oder des Jupiters die aktuellen Richtungen aller Lebewesenkreiszeichen zu bestimmen, auch wenn diese am Himmel gar nicht sichtbar sind, weil sie unter dem Horizont stehen oder vom Sonnenlicht überstrahlt werden.
Im der indischen Astronomie wurde zu diesem Zweck spätestens 500 Jahre danach die Aufteilung in nur '''27 Mondhäuser''' (oder Mondstationen) eingeführt. Da sich die siderische und die synodische Periode um gut zwei Tage unterscheiden, liegen aufeinanderfolgende Neumonde oder Vollmonde stets in verschiedenen Mondhäusern, nach denen im hinduistischen Lunisolarkalender somit die entsprechenden Monate benannt werden können.
Das System mit 28 Mondhäusern wurde etwas später auch von den Altarabern mit '''28 Mondhäusern''' eingesetzt ("Manazil al-Qamar"). Das '''erste Mondhaus''' liegt bei beiden Einteilungen im Frühlingspunkt in der Epoche um Christi Geburt im '''Kopf des Lammes''' beziehungsweise des Widders (Aries) bei den nördlich der Ekliptik liegenden Sternen Scheratan und Hamal (indisch ''Ashvini'' = ''die beiden Rosseschirrenden'' und arabisch ''aš-šaraṭān'' = ''Die beiden Zeichen''). Für das '''zweite Mondhaus''' folgt der '''Bauch des Lammes''' (indisch ''Bharani'' = ''der Wegtragende'' und arabisch ''al-buṭayn'' = ''das Bäuchlein''). Die Plejaden (indisch ''Krittika'' und arabisch ''aṯ-ṯurayyā'') im fetten '''Schwanz des Lammes''' markieren im Anschluss das '''dritte Mondhaus'''. Das '''vierte Mondhaus''' ist durch den roten Riesenstern Aldebaran (arabisch ''al-dabarān'' = ''der Nachfolgende'', indisch ''Rohini'' = ''der Rötliche'') im Sternbild Stier (Taurus) gekennzeichnet.
<gallery caption="Mondstationen" mode="packed" widths="600" heights="600">
Mondhaeuser.Ekliptik.zirkular.png|Die in eine ringförmige Darstellung projizierten 28 Mondhäuser (von 1 bis 28 entgegen dem Uhrzeigersinn) mit den wichtigsten Sternen entlang der Ekliptik (rote gestrichelte Linie '''zur Epoche J0000.0'''). Der Beobachter befindet sich auf der Erde im Zentrum der Darstellung. Nach innen werden die südlichen und nach außen die nördlichen ekliptikalen Breiten gemessen. Die scheinbare Mondbahn pendelt zwischen den beiden zur Ekliptikline benachbarten Hilfslinien. Der Abstand der Hilfslinien beträgt senkrecht zur Ekliptiklinie immer fünf Bogengrad und entlang der Ekliptiklinie immer knapp dreizehn Bogengrad. Mit bis zum Nordpol zunehmender geographischer Breite des Beobachtungspunktes können auch noch knapp ein Bogengrad südlichere ekliptikale Breiten von der Mondscheibe erreicht werden, am Südpol auch noch entsprechend nördlichere ekliptikale Breiten.
Stachelscheibe_Model_zweiseitig.jpg|Die in Niederösterreich gefundene und aus Sandstein gefertigte Gussform für die '''Stachelscheibe von Platt'''.<br/>Von innen nach außen gibt es '''sieben''' konzentrische Kreise, die folgendermaßen zugeordnet werden können:<br/>- Eine große zentrische Bohrung (im Gussteil eine große stachelartige Erhebung für die '''Sonne''').<br/>- Zwölf gleichmäßig verteilte kleine Bohrungen (Zodiak mit '''zwölf''' Sternzeichen sowie für die Umlaufzeit des Planeten '''Jupiter '''in Jahren).<br/>- Drei äquidistante Kreislinien (die drei inneren Planeten '''Merkur''', '''Venus''' und '''Mars''').<br/>- Achtundzwanzig gleichmäßig verteilte kleine Bohrungen ('''Mond'''häuser).<br/>- Ein großer abschließender Kreis ('''Saturn''' als der Langsame und Beständige).
Stonehenge_phase_one.jpg|Die älteste belegte kreisförmige Struktur in Stonehenge 1 (zirka 3100 bis 2900 vor Christi) besteht aus den 56 Aubrey-Löchern (in der Abbildung weiße Kreise). Diese können unter Verwendung von Quadranten, die geographisch durch die vier Himmelsrichtungen in jeweils 14 Mondstationen geteilt sind, dazu verwendet worden sein, die ekliptikale Länge des Mondes in Bezug zum Frühlingspunkt oder zum Herbstpunkt beziehungsweise in Bezug zur Jupiterposition täglich zu markieren (der Mond erreicht den Jupiter ungefähr alle 27,5 Tage). In dieser Zählung wären alle 28 Mondhäuser halbiert, in eine Tagesstation und eine Nachtstation.
</gallery>
Zwischen dem dritten und vierten Mondhaus liegt das Goldene Tor der Ekliptik, wo der Frühlingspunkt zu Beginn der maltesischen Tarxien-Phase lag. Man beachte die fehlenden helleren ekliptiknahen Sterne im '''Trichter der Thuraya''' westlich davon, also rechts der Plejaden (ekliptikale Länge ungefähr 32 Bogengrad) bis hin zum Stern Hydor heutigen Sternbild Wassermann (Aquarius, ekliptikale Länge ungefähr 314 Bogengrad). Die hellsten ekliptiknahen Sterne in diesem Gebiet des Sternenhimmels Alpherg im Sternbild Fische (Pisces) sowie Hydor und Ancha im Sternbild Wassermann (Aquarius) erreichen lediglich die vierte Größenklasse (4<sup>m</sup>), so dass zwischen dem auffälligen offenen Sternhaufen der Plejaden und Deneb Algedi, dem hellsten Stern im Sternbild Steinbock (Capricornus), auf einer Länge von 90 Bogengrad keine hellen ekliptiknahen Sterne vorhanden sind.
→ Zur Einteilung der Ekliptik nach den monatlichen Mondstationen siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Konjunktionen#Mondhäuser|Exkurs '''Mondhäuser''']]
→ Zum dunklen Himmelsquadranten entlang der Ekliptik siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Astronomische_Bezugssysteme#Der_Trichter_der_Thuraya|Exkurs '''Der Trichter der Thuraya''']]
===Drakonitischer Monat===
[[Datei:Drakonitischer.Monat.png|rechts|mini|hochkant=2|Schematische Darstellung der Mondbahn (gelb) im Laufe eines drakonitischen Monats in Bezug auf die Ekliptiklinie (rot). Nach dem Erreichen der südlichsten Lage in Bezug zur Ekliptiklinie wird die Mondbahn aufsteigend, und von der nördlichsten Lage in Bezug auf die Ekliptiklinie wird die Mondbahn dann wieder absteigend. In der deutschsprachigen Schweiz gibt es für diese im Laufe eines drakonitischen Monats täglich mehr oder weniger deutlich wahrnehmbaren Änderungen der ekliptikalen Breite sogar eigene Adjektive. Das Ansteigen der ekliptikalen Breite des Mondes nach Norden wird '''obsigend''' und das Abfallen des Mondes nach Süden '''nidsigend''' genannt. Direkt auf der Ekliptik befinden sich der aufsteigende und der absteigende Knoten der Mondbahn.]]
Der drakonitische Monat ist durch den Verlauf der ekliptikalen Breite des Mondes in Bezug zur Ekliptiklinie beschrieben.
Deswegen gibt es noch den '''drakonitischen Monat''' (altgriechisch ''δράκων'' (''drakon'') beziehungsweise lateinisch ''draco'' = ''Drache''), der eine Dauer von 27,212 Tagen hat. Diese Dauer beschreibt die Zeitpunkte, an denen die um gut 5 Bogengrad zur Ekliptik geneigte Mondbahn die Ekliptik kreuzt; die ekliptikale Breite des Mondes ist dann exakt null. Diese Schnittpunkte werden Mondknoten genannt und werden einmal im Monat im aufsteigenden Mondknoten und einmal im absteigenden Mondknoten erreicht. Befindet sich der Mond auf der Ekliptik, also in der Nähe dieser Mondknoten, kommt es bei dessen Sonnennähe (wenn der Neumond also in Konjunktion mit der Sonne steht) zu einer Sonnenfinsternis und bei dessen Sonnenferne (wenn der Vollmond also in Opposition zur Sonne steht) zu einer Mondfinsternis. Diese Mondpunkte wurden früher als Drachenpunkte bezeichnet, was sich aus der Vorstellung ableitete, dass ein Drache bei einer Mondfinsternis den Mond beziehungsweise bei einer Sonnenfinsternis die Sonne verschlingen würde.
Mit dem folgenden Java-Programm können die ekliptikalen Koordinaten der Sonne und des Mondes für jeden beliebigen Zeitpunkt eines Julianischen Datums in Julianischen Jahrhunderten in Bezug auf die astronomische Standardepoche J2000 berechnet werden:
'''[[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/ EkliptikaleKoordinatenMondSonne|→ Java-Programm "EkliptikaleKoordinatenMondSonne"]]'''<ref>Unter Verwendung der Formeln aus: Oliver Montenbruck, Thomas Pfleger: ''Astronomie mit dem Personal Computer'', Springer-Verlag Berlin Heidelberg GmbH, 1989, ISBN 978-3-662-05865-7</ref>
[[Datei:Ekliptikale.Breiten.Mond.siderischer.Monat.png|links|mini|hochkant=4|Die ekliptikalen Breiten des Mondes im Verlauf eines drakonitischen Monats beziehungsweise eines nur gut zweieinhalb Stunden längeren siderischen Monats mit gut 27 Tagen.]]
{| class="wikitable"
|+ Die täglichen Änderungen der ekliptikalen Breite des Mondes in Bogengrad innerhalb eines Mondviertels
!title="Tage nach aufsteigendem Knoten"| Tage nach<br/>aufsteigendem<br/>Knoten
!title="Änderung der ekliptikalen Breite"| Änderung der<br/>ekliptikalen Breite<br/>zum Vortag
|-
| 1 || 1,2°
|-
| 2 || 1,1°
|-
| 3 || 1,0°
|-
| 4 || 0,9°
|-
| 5 || 0,6°
|-
| 6 || 0,3°
|-
| 7 || 0,0°
|}
<div style="clear:both"></div>
Der Mond kann auf seiner Bahn im Laufe der Zeiten alle ekliptiknahen Himmelsobjekte inklusive aller Planeten und der Sonne bedecken und innerhalb einer Stunde wieder freigeben, die sich in einem Band bis zu gut ±5 Bogengrad nördlich oder südlich neben der Ekliptiklinie befinden.
'''{{w|Aristoteles}}''' (384 bis 322) hat dies in seiner Schrift '''"Über den Himmel"''' (altgriechisch: ''Περὶ οὐρανοῦ'' / ''Peri uranu'') anhand der von ihm beobachteten '''Bedeckung des Planeten Mars durch den zunehmenden Halbmond in der Nähe des Sterns Regulus''' (α Leonis) beschrieben und darauf hingewiesen, dass die Babylonier und die Ägypter solche Phänomene über lange Zeit beobachtet und dokumentiert hatten.<ref>Aristoteles: [http://classics.mit.edu/Aristotle/heavens.2.ii.html On the Heavens], Teil 12, Buch II, um 350 vor Christus, ins Englische übersetzt von John Leofric Stocks (* 1882; † 1937)</ref>
→ Siehe [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Konjunktionen#Aristotelische Mars-Konjunktion|Abschnitt '''Aristotelische Mars-Konjunktion''']].
Der ekliptiknahe Hauptstern Pollux im Sternbild Zwillinge (Gemini) hat sich aufgrund seiner Eigenbewegung im Laufe der letzten zehntausend Jahre so weit von der Ekliptiklinie entfernt, dass er inzwischen nicht mehr vom Mond bedeckt werden kann.
→ Für helle ekliptiknahe Objekte siehe [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Der_Fixsternhimmel#Die_sieben_hellsten_Objekte_der_Ekliptik|Abschnitt '''Die sieben hellsten Objekte der Ekliptik''']].
===Siderische Mondperioden===
Der Mond erscheint innerhalb eines tropischen Jahres dreizehn- oder vierzehnmal an einer bestimmten Stelle des Fixsternhimmels, wobei er wegen der unterschiedlichen Periodendauern von siderischen und synodischen Monaten immer ein anderes '''Mondalter''' (die Anzahl der Tage seit dem letzten Neumond) und wegen der unterschiedlichen Periodendauern von siderischen und drakonitischen Monaten immer eine andere ekliptikale Breite aufweist.
Die beiden folgenden Diagramme sollen den zeitlichen Verlauf der Mondphasen und der ekliptikalen Breiten des Mondes bei seinem Erscheinen im Goldenen Tor der Ekliptik während 254 aufeinanderfolgender siderischer Perioden mit jeweils 27,322 Tagen (insgesamt 6940 Tage beziehungsweise 19 Jahre) veranschaulichen:
[[Datei:Mondphasen.siderische.Perioden.png|links|mini|hochkant=4|Die Mondphasen bei aufeinanderfolgenden Erscheinungen des Mondes im Goldenen Tor der Ekliptik innerhalb von 254 siderischen Perioden (insgesamt 19 Jahre). Da der synodische Monat (von Neumond zu Neumond) über zwei Tage länger ist als der siderische Monat, kommt es hierbei zu Verschiebungen, weil der Mond nach Ablauf eines siderischen Monats beim Erreichen derselben ekliptikalen Länge noch nicht ganz wieder sein maximales Mondalter erreicht hat.<br/><br/>In der oberen Hälfte des Diagramms sind zunehmende und in der unteren Hälfte abnehmende Monde zu beobachten. Eine Mondphase von 0 Prozent steht für einen Neumond und eine Mondphase von ±100 Prozent für einen Vollmond.<br/><br/>Der Startpunkt (Tag 0 im Monat 0) kann zum Beispiel mit dem 22. Mai 2020 am Abend (UTC) angesetzt werden, an dem der Neumond zusammen mit der Sonne im Goldenen Tor der Ekliptik stand. Dies geschieht dann nach 19 Jahren am 23. Mai 2039 kurz nach Mitternacht (UTC) erneut.]]
[[Datei:Ekliptikale.Breiten.Mond.siderische.Perioden.png|links|mini|hochkant=4|Die ekliptikalen Breiten des Mondes bei aufeinanderfolgenden Erscheinungen des Mondes im Goldenen Tor der Ekliptik innerhalb von 254 aufeinanderfolgenden siderischen Perioden (insgesamt 19 Jahre). Da der drakonitische Monat (von einem aufsteigendem Mondknoten bis zum nächsten aufsteigenden Mondknoten) gut zweieinhalb Stunden kürzer ist als der siderische Monat, kommt es hierbei zu Verschiebungen, weil der Mond nach Ablauf eines siderischen Monats den aufsteigenden Knoten bereits wieder hinter sich gelassen hat.<br/><br/>Bei großen ekliptikalen Breiten (oben) kommt es im Goldenen Tor der Ekliptik zu Bedeckungen der Plejaden und bei kleinen ekliptikalen Breiten (unten) kommt es zu Bedeckungen der Hyaden oder des Sterns Aldebaran durch die Mondscheibe.<br/><br/>Der Startpunkt (Tag 0 im Monat 0) kann beispielsweise ebenfalls mit dem 22. Mai 2020 angesetzt werden, an dem der Neumond vom Erdmittelpunkt aus gesehen bei einer ekliptikalen Breite von zirka -2,5 Bogengrad unterhalb der Sonne, deren ekliptikale Breite definitionsgemäß 0 Bogengrad beträgt, im Goldenen Tor der Ekliptik stand.<br/><br/>Nach 18,61 Jahren (beziehungsweise 6793,5 Tagen oder gut 230 synodischen Monaten, in dieser Abbildung also nach gut 248,6 siderischen Monaten) erreicht der Mond dieselbe ekliptikale Breite und fast die gleiche Mondphase, befindet sich dann allerdings bei einer anderen ekliptikalen Länge.<br/><br/>Die kurzperiodische kleine Wellenbewegung kommt durch die Nutation der Erdachse im Bezug zur Ekliptik beziehungsweise zum Fixsternhimmel zustande; sie hat eine Periodendauer von 35 Tagen und überlagert sich mit den zirka eine Woche kürzeren siderischen Mondperiode.]]
<div style="clear:both"></div>
===Der Meton-Zyklus===
Nicht nur die Bestimmung und Vorhersage der Auf- und Untergänge der Venus haben die Aufmerksamkeit der Astronomen des Altertums auf sich gezogen, sondern auch der Mondzyklus mit den verschiedenen Mondphasen sowie das Auftreten von Mondfinsternissen bei Vollmond und von Sonnenfinsternissen bei Neumond. Es gibt einen Zyklus, der die Zeit beschreibt, nachdem die Sonne und der Mond die gleiche Konstellation erreichen. Nach '''19 Jahren''' (beziehungsweise knapp '''6940 Tagen''') hat nicht nur die Sonne dieselbe ekliptikale Länge erreicht, sondern auch der der Mond (nach '''254 siderischen Monaten'''), und er hat daher auch dieselbe Mondphase (nach '''235 synodischen Monaten'''). Außerdem hat er dann auch noch annährend die gleiche ekliptikale Breite (nach '''255 drakonitischen Monaten'''), so dass er fast wieder an derselben Stelle des Fixsternhimmels steht.<ref name="rutherforth">Thomas Rutherforth: "A System Of Natural Philosophy: Being A Course of Lectures In Mechanics, Optics, Hydrostatics, and Astronomy; Which are Read in St Johns College Cambridge", volume 2, chapter XIV: "Of the devision<!--sic!--> of time", paragraph 388: "The cycle of Metos", 990 ff.</ref>
Der Zyklus beruht also im Wesentlichen auf der zwar nur langfristig, bei entsprechender Ausdauer jedoch einfach zu beobachtenden Tatsache, dass 19 tropische Sonnenjahre, 235 synodische Monate, 254 siderische Monate und 255 drakonitische Monate fast die gleiche Länge haben. Der Unterschied zwischen den ersten beiden beträgt nur rund zwei Stunden:
* 19 Jahre = 6939,6 Tage, bestimmt durch die ekliptikale Länge der Sonne mit ihren Äquinoktien und Sonnenwenden.
* 235 synodische Monate = 6939,7 Tage, bestimmt durch die Mondphasen mit Neumonden und Vollmonden.
* 254 siderische Monate = 6939,7 Tage, bestimmt durch die ekliptikale Länge des Mondes.
* 255 drakonitische Monate = 6939,1 Tage, bestimmt durch die auf- und absteigenden Knoten der Mondbahn.
[[Datei:Legende.Meton-Zyklus.png|mini|rechts|Legende zum Diagramm "Meton-Zyklus".]]
<gallery caption="Der Meton-Zyklus" mode="packed">
Meton-Zyklus.Maerz.2019.png|Der Meton-Zyklus anhand der Frühlingsvollmonde (weiße Kreise) am 21. März 2019 (laufende Nummer 0) und am 21. März 2038 (laufende Nummer 235).
Meton-Zyklus.Januar.2023.Januar.2042.png|Der Meton-Zyklus vom Vollmond am 6. Januar 2023 bis zum Vollmond am 6. Januar 2042. Die farbigen Kreise symbolisieren die Äquinoktien und Sonnenwenden.
</gallery>
[[Datei:Meton-Zyklus.2023.13996.png|mini|links|hochkant=4|Der 19-jährige Meton-Zyklus vom Januar 2023 bis zum Januar 2042. Die Zeit schreitet in horizontaler Richtung jeweils um einen halben Tag pro Bildpunkt voran. Der Vollmond (weißer Kreis) erscheint sowohl am 6. Januar 2023 als auch am 6. Januar 2042 im Sternbild Zwillinge (Gemini) nördlich vom Stern Mebsuta (ε Geminorum). Unten sind die kleinen und großen Mondwenden für die geographische Breite 52,5 Bogengrad (zum Beispiel in Berlin) im Laufe der Zeit dargestellt.]]
<div style="clear:both"></div>
Dieser 19-jährige nach dem antiken griechischen Astronomen {{w|Meton}} (5. Jahrhundert vor Christus) benannte '''Meton-Zyklus''' sowie auch der unten erwähnte Saros-Zyklus waren im Altertum spätestens schon den Babyloniern bekannt und dienten als Grundlage für ihren Mondkalender. Meton ist davon ausgegangen, dass 19 Jahre exakt mit 6940 Tagen sowie mit 235 synodischen Monaten übereinstimmen. Dadurch, dass das Jahr nach dieser Annahme genau fünf Neunzehntel Tage länger ist als 365 Tage, sind neunzehn Jahre nach dieser Berechnung genau fünf Tage länger ist als neunzehn Mal 365 Tage, also 6935 Tage. Aus der Annahme einer festen ganzrationalen Kopplung der Umlaufzeiten der Erde um Ihre Achse (Tag) und um die Sonne (Jahr) sowie der Umlaufzeit des Mondes um die Erde (Monat) ergeben sich die folgenden Zusammenhänge:
* Abgerundet auf ganze Zahlen:
** '''Die Jahreslänge in ganzen Tagen:'''
**:<math>\frac {6940} {19} = 365 \text { Rest } 5</math>, das heißt, dass für 19 Jahre mit der Länge 365 Tage '''fünf Schalttage''' (Jahreslänge dann 366 Tage) erforderlich sind, damit der Frühlingspunkt mit dem tropischen Sonnenjahr synchron bleibt (Solarkalender).
** '''Die Monatslänge in ganzen Tagen:'''
**:<math>\frac {6940} {235} = 29 \text { Rest } 125</math>, das heißt, dass für 235 synodische Monate mit der Länge 29 Tage '''125 Schalttage''' (Monatslänge dann 30 Tage) erforderlich sind, damit ein tropisches Sonnenjahr immer zwölf Monate umfasst (Solarkalender).
** '''Die Jahreslänge in ganzen Monaten:'''
**:<math>\frac {\frac {6940} {19}} {\frac {6940} {235}} = \frac {235} {19} = 12 \text { Rest } 7</math>, das heißt, dass in 19 Jahren mit 235 synodischen Monaten sowie 6490 Tagen '''sieben synodische Schaltmonate''' (Jahreslänge dann 13 Monate) erforderlich sind, um das Kalenderjahr mit dem tropischen Sonnenjahr synchron zu halten (Lunisolarkalender).
* Exakt mit Brüchen (ganzrationale Zahlen):
** Die Jahreslänge <math>d_a</math> in Tagen (in einem Sonnenjahr):
**:<math>d_a = \frac {6940 \text { d}} {19 \text { a}} = 365 \frac {\text {d}} {\text {a}} + \frac {5} {19} \frac {\text {d}} {\text {a}} = 365 \frac {\text {d}} {\text {a}} + \frac {20} {76} \frac {\text {d}} {\text {a}} = 365 \frac {\text {d}} {\text {a}} + \frac {1} {4} \frac {\text {d}} {\text {a}} + \frac {1} {76} \frac {\text {d}} {\text {a}} = 365,25 \frac {\text {d}} {\text {a}} + \frac {1} {76} \frac {\text {d}} {\text {a}} \approx 365,263158 \frac {\text {d}} {\text {a}}</math>
** Die Länge eines synodischen Monats <math>d_m</math> in Tagen:
**:<math>d_m = \frac {6940 \text { d}} {235 \text { m}} = \frac {1388} {47} \frac {\text {d}} {\text {m}} = 29 \frac {\text {d}} {\text {m}} + \frac {125} {235} \frac {\text {h}} {\text {m}} = 29 \frac {\text {d}} {\text {m}} + \frac {25} {47} \frac {\text {h}} {\text {m}} \approx 29,531915 \frac {\text {d}} {\text {m}}</math>
** Länge von zwölf synodischen Monaten <math>d_{m_{12}}</math> in Tagen (in einem Mondjahr):
**:<math>d_{m_{12}} = 12 \frac {\text {m}} {\text {a}} \cdot d_m = 12 \frac {\text {m}} {\text {a}} \cdot \frac {1388} {47} \frac {\text {d}} {\text {m}} = \frac {16656} {47} \frac {\text {d}} {\text {a}} \approx 354,382979 \frac {\text {d}} {\text {a}} \approx d_a - 11 \frac {\text {d}} {\text {a}}</math>
** Die Jahreslänge <math>m_a</math> in synodischen Monaten (in einem Sonnenjahr):
**:<math>m_a = \frac {d_a} {d_m} = \frac {\frac {6940 \text { d}} {19 \text { a}}} {\frac {6940 \text { d}} {235 \text { m}}} = \frac {235} {19} \frac {\text {m}} {\text {a}} = 12 \frac {\text {m}} {\text {a}} + \frac {7} {19} \frac {\text {m}} {\text {a}} \approx 12,368421 \frac {\text {m}} {\text {a}}</math>
:'''Anmerkung''': Man nehme zur Kenntnis, dass das der Mittelwert der Dauern vom Sonnenjahr <math>d_a</math> und vom Mondjahr <math>d_{m_{12}}</math> fast genau 360 Tage pro Jahr beträgt, also so viele Tage wie für einen vollständigen Kreis in Bogengrad gerechnet wird:
::<math>\frac {d_a + d_{m_{12}}} {2} = \frac {\frac {6940 \text { d}} {19 \text { a}} + \frac {16656} {47} \frac {\text {d}} {\text {a}}} {2} = \frac {321322} {893} \frac {\text {d}} {\text {a}} \approx 359,823 \frac {\text {d}} {\text {a}} \approx 360 \frac {\text {d}} {\text {a}}</math>
::Mit den heutigen, jeweils rund eine halbe Stunde kürzeren Messwerten für die beiden Jahresdauern (tropisches Sonnenjahr mit 365,241 Tagen und Mondjahr mit zwölf Lunationen und 354,367 Tagen) zur Epoche J2000.0 ergibt sich ein nur geringfügig anderer Mittelwert, der ebenfalls nur um zirka eine Dreiviertelstunde von der Dauer von 360 Tagen abweicht:
::<math>\frac {d_a + d_{m_{12}}} {2} = \frac {365,241 \frac {\text {d}} {\text {a}} + 354,367 \frac {\text {d}} {\text {a}}} {2} \approx 359,804 \frac {\text {d}} {\text {a}} \approx 360 \frac {\text {d}} {\text {a}}</math>
Für diese Erkenntnisse ist entweder die Weitergabe von beobachteten astronomischen Ereignissen, wie der Bedeckung der Plejaden durch den Mond oder die Messung der ekliptikalen Koordinaten des Mondes, an die nächste Generation erforderlich oder ein Lebensalter, das die Beobachtung von mindestens zwei solcher Zyklen umfasst – je nach Zeitpunkt der Geburt also rund 25 bis über 40 Jahre.
Da der Meton-Zyklus mit den aufgerundeten ganzzahligen 6940 Tagen einen Vierteltag länger dauert als 19 Sonnenjahre, summiert sich diese Differenz nach vier Meton-Zyklen auf einen ganzen Tag. Der entsprechende nach {{w|Kallippos von Kyzikos}} (viertes vorchristliches Jahrhundert) benannte '''Kallippische Zyklus''' von 76 Jahren (<math>= 4 \cdot 19</math> Jahre) oder 27759 Tage (<math>= (4 \cdot 6940) - 1</math> Tage) wird auch als verbesserter Meton-Zyklus bezeichnet:
* 76 Jahre = 27758,4 Tage
* 940 synodische Monate = 27758,8 Tage
* 1016 siderische Monate = 27758,8 Tage
* 1020 drakonitische Monate = 27756,5 Tage
Nach ungefähr 48 Sonnenjahren betrug die Differenz zwischen Meton-Zyklus und Sonnenjahr einen Tag, aber erst nach ungefähr 128 Sonnenjahren erreicht die Differenz zwischen Kallippischen Zyklus und Sonnenjahr so groß. Da der Kallippische Zyklus mit genau 27759;Tagen einen Vierteltag länger dauert als 76 Sonnenjahre, summiert sich diese Differenz nach vier Kallippischen Zyklen auf einen ganzen Tag. Der entsprechende nach {{w|Hipparchos (Astronom)|Hipparchos von Nicäa}} (zweites vorchristliches Jahrhundert) benannte '''Hipparchos-Zyklus''' von 304 Jahren (<math>= 4 \cdot 76 = 16 \cdot 19</math> Jahre) = 111035 Tage (<math>= (4 \cdot 27759) - 1</math> Tage) ist also wiederum ein verbesserter Kallippischer Zyklus:
* 304 Jahre = 111033,6 Tage
* 3760 synodische Monate = 111035,0 Tage
* 4064 siderische Monate = 111035,2 Tage
* 4080 drakonitische Monate = 111025,9 Tage
Nach einer Kallippischen Periode beträgt die Differenz zwischen 111035 ganzen Tagen und der Dauer von 3760 synodischen Monaten nur eine dreiviertel Stunde.
Die folgende Tabelle gibt einen Überblick über die Dauern im Meton-Zyklus, im Kallippischen Zyklus und im Hipparchos-Zyklus:
{| class="wikitable"
!
!Tropische Perioden
!Synodische Perioden
!Siderische Perioden
!Ganzzahlige Tage
|-
|'''Dauer einer Periode in Tagen'''
|'''365,2422'''
|'''29,53059'''
|'''27,32166'''
|'''1'''
|-
|
|-
|'''Anzahl der Perioden im Meton-Zyklus'''
|'''19'''
|'''235'''
|'''254'''
|'''6940'''
|-
|'''Anzahl der Tage im Meton-Zyklus'''
|6939,6018
|6939,6887
|6939,7016
|6940
|-
|'''Differenz zu tropischen Perioden in Tagen'''
|0,0000
|0,0868
|0,0998
|0,3982
|-
|'''Differenz zu synodischen Perioden in Tagen'''
| -0,0868
|0,0000
|0,0130
|0,3113
|-
|'''Differenz zu siderischen Perioden in Tagen'''
| -0,0998
| -0,0130
|0,0000
|0,2984
|-
|'''Differenz zu tropischen Perioden in Stunden'''
|0,00
|2,08
|2,40
|9,56
|-
|'''Differenz zu synodischen Perioden in Stunden'''
| -2,08
|0,00
|0,31
|7,47
|-
|'''Differenz zu siderischen Perioden in Stunden'''
| -2,40
| -0,31
|0,00
|7,16
|-
|
|-
|'''Anzahl der Perioden im Kallippischen Zyklus'''
|'''76'''
|'''940'''
|'''1016'''
|'''27759'''
|-
|'''Anzahl der Tage im Kallippischen Zyklus'''
|27758,4072
|27758,7546
|27758,8066
|27759
|-
|'''Differenz zu tropischen Perioden in Tagen'''
|0,0000
|0,3474
|0,3994
|0,5928
|-
|'''Differenz zu synodischen Perioden in Tagen'''
| -0,3474
|0,0000
|0,0520
|0,2454
|-
|'''Differenz zu siderischen Perioden in Tagen'''
| -0,3994
| -0,0520
|0,0000
|0,1934
|-
|'''Differenz zu tropischen Perioden in Stunden'''
|0,00
|8,34
|9,58
|14,23
|-
|'''Differenz zu synodischen Perioden in Stunden'''
| -8,34
|0,00
|1,25
|5,89
|-
|'''Differenz zu siderischen Perioden in Stunden'''
| -9,58
| -1,25
|0,00
|4,64
|-
|
|-
|'''Anzahl der Perioden im Hipparchos-Zyklus'''
|'''304'''
|'''3760'''
|'''4064'''
|'''111035'''
|-
|'''Anzahl der Tage im Hipparchos-Zyklus'''
|111033,6288
|111035,0184
|111035,2262
|111035
|-
|'''Differenz zu tropischen Perioden in Tagen'''
|0,0000
|1,3896
|1,5974
|1,3712
|-
|'''Differenz zu synodischen Perioden in Tagen'''
| -1,3896
|0,0000
|0,2078
| -0,0184
|-
|'''Differenz zu siderischen Perioden in Tagen'''
| -1,5974
| -0,2078
|0,0000
| -0,2262
|-
|'''Differenz zu tropischen Perioden in Stunden'''
|0,00
|33,35
|38,34
|32,91
|-
|'''Differenz zu synodischen Perioden in Stunden'''
| -33,35
|0,00
|4,99
| -0,44
|-
|'''Differenz zu siderischen Perioden in Stunden'''
| -38,34
| -4,99
|0,00
| -5,43
|}
Vor gut 2000 Jahren betrug die Differenz zwischen Kallippischem Zyklus und Sonnenjahr nach ungefähr 227 Sonnenjahren einen Tag. Durch die inzwischen etwas verkürzte Dauer eines tropischen Jahres ist dies heute bereits nach etwa 221 Jahren der Fall.
====Die Goldene Zahl====
Die '''Goldene Zahl''' gibt an, das wievielte von diesen 19 Jahren ein bestimmtes Jahr ist, und sie spielt auch heute noch eine wichtige Rolle bei der Bestimmung des Datums von Pessach oder Ostern, zum Beispiel mit Hilfe der Formeln zur Berechnung von {{w|Carl Friedrich Gauß}} (* 1777; † 1855):
→ Siehe auch:
* [[Astronomie von der Frühgeschichte bis zur Neuzeit/ Kalenderführung/ Osterdatum|'''Osterdatum''']]
* {{w|Gaußsche Pessach-Formel}}
* {{w|Gaußsche Osterformel}}
Der Name Goldene Zahl rührt möglicherweise davon her, dass der diesem Zyklus zugrundeliegende Kalender ('''Parapegma''') des Meton auf den Steinmauern seiner Sonnenuhr (heliotropion) am Pnyx-Hügel in Athen in goldener Schrift zu sehen war.<ref>Michael Wright: [https://www3.astronomicalheritage.net/index.php/show-entity?identity=26&idsubentity=1 The Pnyx, Athens, Greece], Portal to the Heritage of Astronomy, August 2011</ref><ref name="rutherforth" />
Heute ist in den Monaten um die Wintersonnenwende alle 19 Jahre morgens am westlichen Horizont der untergehende Vollmond im Goldenen Tor der Ekliptik zu sehen, wie im Dezember 2018. Die untere Hälfte des Mondes wird dann während des Untergangs vom Horizont verdeckt und der sichtbare leuchtende Teil bildet somit einen Halbkreis, wie er im mittleren Segment der Himmelstafel angedeutet ist. In diesem Fall liegen Hyaden und Plejaden im Westen auf einer Linie parallel zum Horizont und der dazwischenliegende, beim Untergang noch halb zu sehende Vollmond würde der Abbildung auf der Steintafel von Tal-Qadi entsprechen. Vor 4500 Jahren ergab sich diese Himmelsansicht wegen der Verschiebung des Frühlingspunktes bereits um die Tag-und-Nacht-Gleiche im Herbst.
Die 19 Megalithe des Blaustein-Hufeisens von '''Stonehenge''' (2270 bis 1930 vor Christus) werden ebenfalls mit dem 19-jährigen Meton-Zyklus in Zusammenhang gesehen.
Im Übrigen werden auch die Goldhüte aus der Bronzezeit mit diesem Zyklus in Verbindung gebracht. Der '''Berliner Goldhut''' zeigt in der siebenten und in der neunten ringförmigen Zone von oben horizontale Reihen mit 57 (= 3 x 19) kreisförmigen Punzen, die für ein Viertel der 228 Monate einer Meton-Periode stehen könnten.<ref>Wilfried Menghin: „Der Berliner Goldhut und die goldenen Kalendarien der alteuropäischen Bronzezeit“, Acta Praehistorica et Archaeologica, Band 32, 2000, ISSN 0341-1184, Seiten 31 bis 108</ref>
An der östlichen Wand in der Kamme A des hethitischen Heiligtums '''Yazılıkaya''' (türkisch für „beschriebener Fels“) aus dem 13. vorchristlichen Jahrhundert existiert eine Reliefbilderreihe, die eine Prozession von '''neunzehn''' nach links schauenden Göttinnen im Ganzkörperprofil darstellt. Auch hier wird vermutet, dass diese Reihe als Zählwerk für den Meton-Zyklus eine Kalenderfunktion innehatte.<ref>Eberhard Zangger, Rita Gautschy: [http://63.33.38.154/JSA/article/view/12232 Celestial Aspects of Hittite Religion - An Investigation of the Rock Sanctuary Yazilikaya], Journal of Skyscape Archaeology, 5(1), 5–38, 2019</ref><ref>Edwin C. Krupp, Eberhard Zangger: [https://www.archaeologie-online.de/artikel/2021/die-symbolische-darstellung-des-kosmos-im-hethitischen-felsheiligtum-yazlkaya Die symbolische Darstellung des Kosmos im hethitischen Felsheiligtum Yazılıkaya] vom 16. Juni 2021, Archäologie Online, archaeomedia, Freiburg</ref>
Der Zyklus mit der Anzahl 19 taucht in weiteren archäoastronomischen Kontexten auf:
* Mond-Sonne-Darstellung aus dem mesoamerikanischen '''Codex Borbonicus''' der Azteken vom Anfang des 16. Jahrhunderts.
* [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Mondzyklen#Der_Kalenderstein_vom_Tempel_Mnajdra|Oberste Lochreihe auf dem östlichen Kalenderstein vom Tempel '''Mnajdra''' auf Malta]].
* [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Höhlenmalerei_in_der_Magura-Höhle#Kalender|Kalender unter den Höhlenmalereien in der '''Magura'''-Höhle mit einer Matrix aus 19 Punkten]].
<gallery caption="Die Zahl 19 in archäoastronomischen Kontexten" mode=packed widths=360 heights=360>
Magura_cave_calendar_1528.jpg|Detail mit Elementen eines Kalenders aus der '''Magura'''-Höhle in Bulgarien. Unten in der Mitte befindet sich ein Feld mit 19 Punkten.
Kalenderstein.Mnajdra.labelled.png|Skizze der Lochreihen auf dem Kalenderstein von '''Mnajdra''' auf Malta. Die oberste Lochreihe besteht aus 19 Löchern.
Hattusa,_capital_of_the_Hittite_Empire_51.jpg|Detail mit den linken drei der insgesamt neunzehn Göttinnen der Bilderreihe in der Kammer A des hethitischen Heiligtums '''Yazılıkaya'''.
Yazilikaya.19.Gottheiten.png|Schmatische Darstellung der 19 weiblichen Gottheiten im hethitischen Heiligtum '''Yazılıkaya''' in der Türkei. Im Original variieren die Darstellungen der einzelnen Figuren, die in unterschiedlichem Zustand erhalten sind. Über den Händen einzelner Figuren konnten verschiedene hieroglyphische Darstellungen festgestellt werden. Die erste Figur (ganz links) konnte hierdurch nur indirekt rekonstruiert werden.
Berliner.Goldhut.P1068203.jpg||'''Berliner Goldhut''' vom Ende der Bronzezeit (1000–800 vor Christus) im Neuen Museum.
MesoamerikanischerMond.CodexBorbonicus.png|Mond-Sonne-Darstellung aus dem mesoamerikanischen '''Codex Borbonicus''' der Azteken vom Anfang des 16. Jahrhunderts. Die beiden sich zu einem vollen Kreis ergänzenden Halbkreise symbolisieren den Mond und die Sonne. Im Halbkreis des Mondes befinden sich '''19 Kreise'''.
</gallery>
====MUL.APIN====
[[Datei:MUP.APIN.VAT09412.P1078712.jpg|thumb|1914 gefundene Tontafel mit Texten aus dem 687 vor Christus geschrieben babylonischen Kompendium MUL.APIN, Irak, Assur/Qal'at Scherqat, VAT 09412, Neues Museum Berlin]]
Die auf den MUL.APIN-Tafeln schriftlich überlieferten babylonischen [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Plejaden#Schaltregeln|'''Plejaden-Schaltregeln''']] beziehen sich zweifelsfrei auf einen Zeitpunkt, als der offene Sternhaufen der Plejaden im Sternbild Stier (Taurus) die gleiche ekliptikale Länge wie der Frühlingspunkt gehabt hat. Dies bedeutet, dass die Sonne zur Tag-und-Nacht-Gleiche direkt neben den Plejaden stand, und dies war um 2600 vor Christus der Fall. Aus beiden Schaltregeln ergibt sich, dass innerhalb von 19 Sonnenjahren sieben zusätzliche synodische Monate eingeschaltet werden müssen, damit Sonnen- und synodisches Mondjahr synchron bleiben. Die Differenz zwischen Mondlauf und Sonnenlauf beträgt nach diesen 19 Sonnenjahren nur gut zwei Stunden. Diese Abweichung beträgt nur 0,0125 Promille beziehungsweise 12,58 parts per million (ppm):
* Tropisches Sonnenjahr (von Frühlingspunkt zu Frühlingspunkt): <math>J_S = 365,2422 \text { d} = 8765,813 \text { h}</math>
* Synodischer Monat (von Neumond zu Neumond): <math>M_{syn} = 29,53059 \text { d} = 708,7342 \text { h}</math>
* Synodisches Mondjahr (zwölf synodische Monate): <math>J_{syn} = 12 \cdot M_{syn} = 354,3671 \text { d} = 8504,810 \text { h}</math>
* Jahresdifferenz zwischen tropischem Sonnenjahr und synodischem Mondjahr: <math>\Delta J = J_S - J_{syn} = 10,8751 \text { d} = 261,003 \text { h}</math>
* Meton-Zyklus: <math>19 \cdot \Delta J = 206,627 \text { d} = 4959,046 \text { h}</math> beziehungsweise <math>7 \cdot M_{syn} = 206,714 \text { d} = 4961,139 \text { h}</math>
* Genauigkeit des Meton-Zyklus: <math>\Delta M = 7 \cdot M_{syn} - 19 \cdot \Delta J = 0,087 \text { d} \approx 2,09 \text { h} = 235 \cdot M_{syn} - 19 \cdot J_S</math>
* Arithmetische Zusammensetzung und Primfaktorenzerlegung der Anzahl synodischer Mondzyklen: <math>235 = 7 + 19 \cdot 12 = 5 \cdot 47</math>
Mit anderen Worten war der Meton-Zyklus bereits über zwei Jahrtausende lang bekannt, bevor sich der griechische Astronom Meton damit beschäftigte.
Es ist äußerst bemerkenswert, dass eine entsprechende Berechnung auch für dreizehn siderische Monate durchgeführt werden kann, bei denen der Mond jeweils einmal vollständig die Ekliptik mit ihren zwölf Ekliptiksternbildern beziehungsweise den 27 oder 28 Mondstationen (auch Mondhäuser genannt, siehe auch [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Konjunktionen#Mondhäuser|Exkurs '''Mondhäuser''']]) durchläuft:
* Tropisches Sonnenjahr (von Frühlingspunkt zu Frühlingspunkt): <math>J_S = 365,2422 \text { d} = 8765,813 \text { h}</math>
* Siderischer Monat (von Fixstern zu Fixstern): <math>M_{sid} = 27,32166 \text { d} = 655,720 \text { h}</math>
* Siderisches Mondjahr (13 siderische Monate): <math>J_{sid} = 13 \cdot M_{sid} = 355,1816 \text { d} = 8524,358 \text { h}</math>
* Jahresdifferenz zwischen tropischem Sonnenjahr und siderischem Mondjahr: <math>\Delta J = J_S - J_{sid} = 10,0606 \text { d} = 241,454 \text { h}</math>
* Meton-Zyklus: <math>19 \cdot \Delta J = 191,151 \text { d} = 4587,634 \text { h}</math> beziehungsweise <math>7 \cdot M_{sid} = 191,252 \text { d} = 4590,040 \text { h}</math>
* Genauigkeit des Meton-Zyklus: <math>\Delta M = 7 \cdot M_{sid} - 19 \cdot \Delta J = 0,100 \text { d} \approx 2,40 \text { h} = 254 \cdot M_{sid} - 19 \cdot J_S</math>
* Arithmetische Zusammensetzung und Primfaktorenzerlegung der Anzahl siderischer Mondzyklen: <math>254 = 7 + 19 \cdot 13 = 2 \cdot 127</math>
Auch hier beträgt die Differenz zwischen Mondlauf und Sonnenlauf nur gut zwei Stunden. Und innerhalb der 19 Sonnenjahre eines Meton-Zyklus müssen auch hier sieben zusätzliche (vierzehnte) siderische Monate eingeschaltet werden, damit Sonnen- und siderisches Mondjahr synchron bleiben.
Zur Differenz von knapp elf Tagen zwischen Sonnen- und Mondjahr siehe auch '''[[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Plejaden#Das_babylonische_Neujahrsfest_Akiti|das babylonische Neujahrsfest Akiti]]'''. Dieses elftägige Fest wurde am Ende des Mondjahres gefeiert.
===Der drakonitische Zyklus===
Ferner existiert ein zirka '''18,61-jähriger Mondzyklus''', der darauf beruht, dass bedingt durch die Präzession der Mondbahn der aufsteigende und der absteigende Mondknoten nach dieser Zeit die Ekliptik entgegen der rechtläufigen (prograden) Umlaufrichtung des Mondes genau einmal vollständig rückläufig (retrograd) durchlaufen haben. Dieser Zyklus besteht aus 249,83 drakonitischen Monaten, die insgesamt 6798,38 Tagen beziehungsweise 18,61 tropischen Sonnenjahren entsprechen. Die ekliptikalen Längen der Mondknoten vermindern sich hierbei um einen Winkel von 19,34 Bogengrad pro Jahr.
Dieser drakonitische Zyklus ist zum Beispiel anhand der Abweichungen der ekliptikalen Breiten des Mondes und somit der Azimute bei den monatlichen Mondaufgängen und Monduntergängen am Horizont zu beobachten, die sich nach 18,61 Jahren wiederholen und dabei um die Punkte der Wintersonnenwende im Südosten und Südwesten sowie die Punkte der Sommersonnenwende im Nordosten und Nordwesten entlang dem Horizont pendeln. Die Zeitpunkte an dem die entsprechenden Auf- und Untergangspunkte zwischen dem nördlichen und dem südlichen Horizont um die Punkte der Tag-und-Nacht-Gleichen im Osten und Westen, die definitionsgemäß bei der ekliptikalen Breite null genau in der Ekliptik liegen, am engsten beziehungsweise am weitesten auseinanderliegen, heißen '''große und kleine Mondwenden'''. Diese Mondwenden wiederholen sich also alle 18,61 Jahre. Hierbei treten auch größte und kleinste Mondwenden auf. Um die Wintersonnenwenden der Jahre 2023, 2042, 2061 und so weiter treten beispielsweise '''größte Mondwenden''' auf, bei denen die Auf- und Untergangspunkte maximal in Richtung Norden von den genau im Osten und im Westen liegenden Äquinoktialpunkten entfernt sind. Und um die Sommersonnenwenden der Jahre 2024, 2043, 2062 und so weiter treten '''kleinste Mondwenden''' auf, bei denen die Auf- und Untergangspunkte maximal in Richtung Süden von den genau im Osten und im Westen liegenden Äquinoktialpunkten entfernt sind.
[[Datei:Ekliptik.helle.Objekte.png|mini|hochkant=2|rechts|Die sieben hellsten feststehenden Himmelsobjekte in der Nähe der Ekliptik liegen zwischen den Sternbildern Stier (Taurus, rechts) und Skorpion (Scorpio, links). Der Bogen der Ekliptik wird von den Wandelgestirnen entgegen dem Uhrzeigersinn vom Frühlingspunkt rechts zum Herbstpunkt links durchlaufen. In der Nähe unteren Bogenhälfte befinden sich keine hellen Fixsterne in der Nähe der Ekliptik, der helle Stern Fomalhaut (α Piscis Austrini) im Sternbild Südlicher Fisch (Piscis Austrinus) dient lediglich zur Orientierung. Außerhalb des Bogens liegende Punkte befinden sich nördlich der Ekliptik und innen liegende südlich.]]
Aufgrund dieser Zusammenhänge werden alle möglichen Positionen des Mondes in Bezug auf die Ekliptik bei den ekliptikalen Längen von -180 bis +180 Bogengrad und den ekliptikalen Breiten von ungefähr -6 bis +6 Bogengrad innerhalb dieser 18,61-jährigen Periode erreicht. Somit erfolgen auch alle möglichen Sternbedeckungen (Okkultationen) oder nahe Konjunktionen innerhalb dieser Periodendauer und wiederholen sich danach im drakonitischen Zyklus. Die Bedeckungen hellsten ekliptiknahen Himmelsobjekte sind hierbei besonders spektakulär und gut zu beobachten. Dies gilt insbesondere für:
* die '''Plejaden''' (Messier 45, 1,5<sup>m</sup>) im Sternbild Stier (Taurus)
* die '''Hyaden''' (0,5<sup>m</sup>) im Sternbild Stier (Taurus)
* den Roten Riesen '''Aldebaran''' (α Tauri, 1,0<sup>m</sup>) im Sternbild Stier (Taurus)
* den Roten Überriesen '''Antares''' (α Scorpii, 1,0<sup>m</sup>) im Sternbild Skorpion (Scorpio),
* den Stern '''Spica''' (α Virginis, 1,0<sup>m</sup>) im Sternbild Jungfrau (Virgo)
* den Stern '''Regulus''' (α Leonis, 1,5<sup>m</sup>) im Sternbild Löwe (Leo)
Wenn der Mond bei der Bedeckung der '''Plejaden''' seine maximale nördliche ekliptikale Breite bereits zuvor erreicht hatte und sich also bereits wieder in Richtung seines absteigenden Knotens bewegt, befindet sich in der Nähe des absteigenden Knotens der Königsstern '''Regulus''' im Sternbild Löwe (Leo), so dass es ungefähr eine Woche später ebenfalls zu dessen Bedeckung durch den Mond kommen kann. Da der Stern '''Antares''' α Scorpii) im Sternbild Skorpion (Scorpio) in etwa die gleiche südliche ekliptikale Breite hat, wie die Plejaden eine nördliche ekliptikale Breite haben, und beide auf gegenüberliegenden Punkten der Ekliptiklinie liegen, kommt es im Rhythmus des drakonitischen Zyklus innerhalb eines halben Monats gehäuft zu der Bedeckung beider Himmelsobjekte durch die Mondscheibe. Beide Ereignisse wären wegen der gegenüberliegenden Lage auf der Ekliptik heute allerdings nur während der langen Nächte im Winterhalbjahr zu sehen, wenn Antares praktisch nicht zu beobachten ist, weil er von der Sonne überstrahlt wird. Als der Frühlingspunkt im Altertum jedoch im Sternbild Stier stand, war die aufeinanderfolgende Bedeckung von Plejaden und Antares jedoch durchaus ein zu beobachtendes Doppelereignis.
Das nächste Mal werden die beiden eng benachbarten Elternsterne der Plejaden (der Titan Atlas und die Okeanide Pleione) von Mitteleuropa aus gesehen in den Morgenstunden des 8. Augusts 2024 von der Scheibe des abnehmenden Halbmonds bedeckt. Am 1. April 2025 werden gegen Mitternacht dann sogar mehrere helle Sterne des Sternhaufens durch die nur vier Tage alte Mondsichel bedeckt.
Auch im alten chinesischen, mündlich überlieferten Volksmärchen „Morgenhimmel“ wird der Zyklus vom '''Stern des großen Jahres''' erwähnt, der sich erst nach 18 Jahren, also im 19. Jahr wiederholt:<ref>[[s:Morgenhimmel|Morgenhimmel]], Wikisource</ref>
<blockquote>
Als Morgenhimmel gestorben war, berief der Kaiser den Sterndeuter und fragte: „Kanntest du Morgenhimmel?“<br/>
Der sagte: „Nein.“<br/>
Der Kaiser fragte: „Was verstehst du denn?“<br/>
Der Sterndeuter sagte: „Ich kann nach den Sternen sehen.“<br/>
„Sind alle Sterne an ihrem Platz?“ fragte der Kaiser.<br/>
„Ja. Nur den Stern des großen Jahres habe ich achtzehn Jahre nicht gesehen. Jetzt aber ist er wieder sichtbar.“<br/>
Da blickte der Kaiser zum Himmel auf und seufzte: „Achtzehn Jahre lang war Morgenhimmel mir zur Seite, und ich wusste nicht, dass er der Stern des großen Jahres war.“
</blockquote>
Mit "Stern des großen Jahres" könnte ein Ereignis gemeint sein, bei dem der Mond alle 18,61 Jahre einen bestimmten hellen und ekliptiknahen Stern bedeckt, wie zum Beispiel einen der drei Königssterne Aldebaran (α Tauri) im Sternbild Stier (Taurus), Regulus (α Leonis) im Sternbild Löwe (Leo), Antares (α Scorpii) im Sternbild Skorpion (Scorpio) oder auch Spica (α Virginis) im Sternbild Jungfrau (Virgo).
Auch für den drakonitischen Zyklus kann für dreizehn drakonitische Monate eine Berechnung in Bezug auf den 19-jährigen Meton-Zyklus durchgeführt werden:
* Tropisches Sonnenjahr (von Frühlingspunkt zu Frühlingspunkt): <math>J_S = 365,2422 \text { d} = 8765,813 \text { h}</math>
* Drakonitischer Monat (von Mondknoten zu Mondknoten): <math>M_{dra} = 27,21222 \text { d} = 653,093 \text { h}</math>
* Drakonitisches Mondjahr (13 drakonitische Monate): <math>J_{dra} = 13 \cdot M_{dra} = 353,7589 \text { d} = 8490,213 \text { h}</math>
* Jahresdifferenz zwischen tropischem Sonnenjahr und drakonitischem Mondjahr: <math>\Delta J = J_S - J_{dra} = 11,4833 \text { d} = 275,600 \text { h}</math>
* Meton-Zyklus: <math>19 \cdot \Delta J = 218,1835 \text { d} = 5236,403 \text { h}</math> beziehungsweise <math>8 \cdot M_{sid} = 217,6978 \text { d} = 5224,746 \text { h}</math>
* Genauigkeit des Meton-Zyklus: <math>\Delta M = 8 \cdot M_{dra} - 19 \cdot \Delta J = -0,486 \text { d} \approx -11,66 \text { h} = -(255 \cdot M_{dra} - 19 \cdot J_S)</math>
* Arithmetische Zusammensetzung und Primfaktorenzerlegung der Anzahl drakonitischer Mondzyklen: <math>255 = 8 + 19 \cdot 13 = 3 \cdot 5 \cdot 17</math>
Hier beträgt die Differenz zwischen Mondlauf und Sonnenlauf knapp einen halben Tag. Innerhalb der 19 Sonnenjahre eines Meton-Zyklus müssen hier acht zusätzliche (vierzehnte) drakonitische Monate eingeschaltet werden, damit Sonnen- und drakonitisches Mondjahr synchron bleiben.
===Der Saros-Zyklus===
[[Datei:Saros-Zyklus.2023.Januar.png|rechts|mini|hochkant=4|Der '''Saros-Zyklus''' umfasst sowohl eine Periode von 242 drakonitischen Monaten als auch eine Periode von 223 synodischen Monaten. Das sind gut 6585 Tage oder 18,03 tropische Sonnenjahre, also 18 Jahre und knapp 11 Tage). In der Graphik ist die Zeit vom aufsteigenden Knoten (rot) am 1. Januar 2023 bis zum aufsteigenden Knoten (rot) am 11. Januar 2041 dargestellt. Man beachte die gleichzeitige zyklische Wiederholung der jeweiligen Mondphasen sowie daraus folgend der '''Finsternisse''' im April und Mai sowie im Oktober und November mit einer Verschiebung von elf Kalendertagen im Sonnenjahr.<br/>Die '''Mondwenden''' (die tagesaktuellen Auf- und Untergangsazimute des Mondes sind im unteren Bereich für die geographische Breite 52,5 Bogengrad (zum Beispiel in Berlin) dargestellt) haben ihr Minimum und Maximum demzufolge zu den entsprechenden Zeitpunkten, die Ausprägung dieser Extrema unterscheidet sich jedoch ein wenig, weil der Mond dort geringfügig abweichende minimale und maximale ekliptikale Breiten hat. Die kleinen Mondwenden beim Sommervollmond im Juli und die großen Mondwenden beim Wintervollmond (Jahreswechsel) sind in der Graphik ebenfalls nachvollziehbar.<br/>[[Datei:Legende.Meton-Zyklus.png|200px|zentriert|Legende]]]]
Über die weiter oben beschriebenen Koinzidenzen hinaus kann beobachtet werden, dass der Mond nach '''18,03 Jahren''' (also nach 242 drakonitischen Monaten beziehungsweise 6585,3 Tagen) denselben auf- oder absteigenden Knoten erreicht, wobei Sonne und Mond dann die gleiche Elongation haben (nach 223 synodischen Monaten beziehungsweise 6585,2 Tagen). Sie befinden sich dann allerdings nur fast bei den gleichen ekliptikalen Längen beziehungsweise an den gleichen Stellen des Fixsternhimmels, da diese Dauer nur mit ungefähr einem halben Tag Differenz mit 241 siderischen Perioden übereinstimmt (6584,6 Tage). Innerhalb dieses halben Tages hat sich die Sonne um zirka ein halbes Bogengrad und der Mond sogar um ungefähr sechseinhalb Bogengrad weiterbewegt.
Dieser Zyklus wird '''Saros-Zyklus''' genannt. Innerhalb dieser Zeitspanne ergibt sich eine Reihe von Sonnen- und Mondfinsternissen, die sich in ihrer Abfolge immer wieder ähneln. Der Saros-Zyklus war bei den Babyloniern bereits im Altertum bekannt, was in Keilschrift geschrieben auf Tontafeln aus der Mitte des achten vorchristlichen Jahrhunderts überliefert ist.
Heute werden die sich wiederholenden Sonnen- und Mondfinsternisse, die sich auf einen bestimmten auf- oder absteigenden Knoten der Mondbahn beziehen und jeweils eine Lebensdauer von mehreren Jahrhunderten haben, zum Zwecke der Unterscheidung mit Nummern versehen. Zu einem bestimmten Zeitpunkt der Weltgeschichte sind immer mehrere Dutzend Saros-Zyklen gleichzeitig aktiv.
Die anomalistische Periode steht für die Dauer, bis der Mond auf seiner elliptischen Bahn um die Erde erneut eines seiner beiden Apogäen erreicht, also das Perigäum (erdnächster Punkt) oder das Apogäum (erdfernster Punkt). Nach einer ganzzahligen vielfachen Dauer der anomalistischen Periode hat der Mond also stets fast die gleiche Entfernung zur Erde und somit auch die gleiche scheinbare Größe, so dass die Dauer und Art (totale oder ringförmige Sonnenfinsternis) einer Finsternis sich wiederholt.
Wegen der gleichen ekliptikalen Breiten des Mondes nach einer ganzzahligen vielfachen Dauer der drakonitischen Periode erscheint der Mondschatten auf der Erdoberfläche nach der Dauer einer Saros-Periode ungefähr beim gleichen Breitengrad. Durch die Differenz von rund einem drittel Tag ist der Längengrad allerdings nach einer Saros-Periode um knapp 120 Bogengrad nach Westen verschoben. Nach drei Saros Perioden, oder nach einem dadurch definierten Exeligmos-Zyklus, haben sich die drei Dritteltage fast auf einen Tag summiert, so dass nach der Mondschatten dann nicht nur wieder beim ähnlichen Breitengrad, sondern sogar beim ähnlichen Längengrad und zu einer ähnlichen Tageszeit auftaucht. Anders ausgedrückt: wenn an einem bestimmten Ort auf der Erdoberfläche eine Finsternis auftritt, so erfolgt in der Regel nach 19756 Tagen (54 Jahren und 33 Tagen) in dieser geographischen Region mit nur rund einer Stunde Verfrühung erneut eine solche Finsternis.
{| class="wikitable"
|+ Exeligmos-Zyklus
! Anzahl der Saros-Zyklen !! Dauer in tropischen Jahren !! Dauer in ganzen Tagen
|-
| 1 || 18,03 || 6585
|-
| 3 || 54,09 || 19756
|}
Die folgende Tabelle gibt einen Überblick über die Dauern verschiedener Mondzyklen in einem '''Saros-Zyklus''' (18,03 tropische Jahre) sowie in einem dreimal so langen Zyklus '''Exeligmos-Zyklus''' (54,09 tropische Jahre) .
{| class="wikitable"
!
!Synodische Perioden
!Drakonitische Perioden
!Tage
!Anomalistische Perioden
|-
|'''Dauer einer Periode in Tagen'''
|'''29,53059'''
|'''27,21222'''
|'''1'''
|'''27,53455'''
|-
|
|-
|'''Anzahl der Perioden im Saros-Zyklus'''
|'''223'''
|'''242'''
|'''6585'''
|'''239'''
|-
|'''Anzahl der Tage im Saros-Zyklus'''
|6585,3216
|6585,3572
|6585,0000
|6580,7575
|-
|'''Differenz zu synodischen Perioden in Tagen'''
|0,0000
|0,0357
| -0,3216
| -4,5641
|-
|'''Differenz zu drakonitischen Perioden in Tagen'''
| -0,0357
|0,0000
| -0,3572
| -4,5998
|-
|'''Differenz zu ganzen Tagen'''
|0,3216
|0,3572
|0,0000
| -4,2425
|-
|'''Differenz zu synodischen Perioden in Stunden'''
|0,00
|0,86
| -7,72
| -109,54
|-
|'''Differenz zu drakonitischen Perioden in Stunden'''
| -0,86
|0,00
| -8,57
| -110,39
|-
|'''Differenz zu ganzen Tagen in Stunden'''
|7,72
|8,57
|0,00
| -101,82
|-
|
|-
|'''Anzahl der Perioden im Exeligmos-Zyklus'''
|'''669'''
|'''726'''
|'''19756'''
|'''717'''
|-
|'''Anzahl der Tage im Exeligmos-Zyklus'''
|19755,9647
|19756,0717
|19756,0000
|19742,2724
|-
|'''Differenz zu synodischen Perioden in Tagen'''
|0,0000
|0,1070
|0,0353
| -13,6924
|-
|'''Differenz zu drakonitischen Perioden in Tagen'''
| -0,1070
|0,0000
| -0,0717
| -13,7994
|-
|'''Differenz zu ganzen Tagen in Tagen'''
| -0,0353
|0,0717
|0,0000
| -13,7277
|-
|'''Differenz zu synodischen Perioden in Stunden'''
|0,00
|2,57
|0,85
| -328,62
|-
|'''Differenz zu drakonitischen Perioden in Stunden'''
| -2,57
|0,00
| -1,72
| -331,18
|-
|'''Differenz zu ganzen Tagen in Stunden'''
| -0,85
|1,72
|0,00
| -329,46
|}
===Mondwenden===
Da sich die '''Sonne''' definitionsgemäß immer in der Ekliptikebene befindet, wiederholen sich die Verhältnisse in Bezug auf ihre Auf- und Untergangspunkte sowie auf ihre südlichen und die nördlichen Kulminationshöhen in jedem tropischen Sonnenjahr. Zur '''Sommersonnenwende''' steht die Sonne am höchsten und am längsten über den Horizont, die Auf- und Untergangsazimute erreichen ihre nördlichsten Positionen und es ergibt sich der Kalendertag mit dem längsten Tagesslicht und der kürzesten Dunkelheit. Zur '''Wintersonnenwende''' steht die Sonne am niedrigsten und am kürzesten über den Horizont, die Auf- und Untergangsazimute erreichen ihre südlichsten Positionen und es ergibt sich der Kalendertag mit dem kürzesten Tagesslicht und der längsten Nacht. Bei den beiden '''Äquinoktien''' zum '''Frühlings- und Herbstbeginn''' herrscht die '''Tag-und-Nacht-Gleiche''', zwölf Stunden Tag und zwölf Stunden Nacht. Die Sonne geht überall auf der Erde exakt im Osten auf beziehungsweise im Westen unter.
Mit dem Neumond verhält es sich ähnlich wie mit der Sonne, da er sich stets in Sonnennähe befindet. Die Mondbahn ist gegenüber der Ekliptik jedoch geneigt (Inklination der Mondbahn), so dass der Mond bis zu gut 5 Bogengrad höher oder niedriger stehen kann als die Sonnenscheibe. Bei einer '''großen Mondwende''' steht die Ekliptik immer besonders hoch über dem Horizont und der Mond hat gleichzeitig seine maximale nördliche ekliptikale Breite erreicht. Wenn der Vollmond bei einer großen Mondwende auf dem südlichen Meridian kulminiert, findet dieses Ereignis als '''größte Mondwende''' um Mitternacht zur Wintersonnenwende statt (im Dezember 2023, 2042 und 2061).
[[Datei:Grosse.Mondwende.png|mini|zentriert|hochkant=2|Die größte Mondwende bei der am höchsten stehenden Ekliptik auf der Nordhalbkugel sowie die Sommersonnenwende im Horizontsystem. Die blaue Mondbahn wird während der Sommersonnenwende nur bei Neumond gleichzeitig mit der orangefarbenen Sonnenbahn durchlaufen. Während der Wintersonnenwende findet diese große Mondwende statt, wenn der Mond in Opposition zur Sonne steht und somit Vollmond herrscht.]]
Bei einer '''kleinen Mondwende''' steht die Ekliptik immer besonders niedrig über dem Horizont und der Mond hat gleichzeitig seine minimale südliche ekliptikale Breite erreicht. Wenn der Vollmond bei einer kleinen Mondwende auf dem südlichen Meridian kulminiert, findet dieses Ereignis als '''kleinste Mondwende''' um Mitternacht zur Sommersonnenwende statt (im Juni 2024, 2043 und 2062).
[[Datei:Kleine.Mondwende.png|mini|zentriert|hochkant=2|Die kleinste Mondwende bei der am niedrigsten stehenden Ekliptik auf der Nordhalbkugel sowie die Wintersonnenwende im Horizontsystem. Die blaue Mondbahn wird während der Wintersonnenwende nur bei Neumond gleichzeitig mit der orangefarbenen Sonnenbahn durchlaufen. Während der Sommersonnenwende findet diese kleine Mondwende statt, wenn der Mond in Opposition zur Sonne steht und somit Vollmond herrscht.]]
[[Datei:ZunehmenderMond.Herbst-Aequinoktium.P1185517.jpg|mini|rechts|Zunehmender Mond zum Herbst-Äquinoktium bei kleiner Mondwende mit flacher Ekliptik in Berlin mit fast senkrecht stehender Mondsichel (38% beleuchtet) im Sternbild Skorpion kurz vor Sonnenuntergang. Visuelle Helligkeit -9 mag, südliche ekliptikale Breite = -5°, Höhe über dem südlichen Horizont = 9°, Mondalter 6,2 Tage.]]
In der folgenden Übersicht sind die '''Mondaufgangspunkte''' am östlichen Horizont, die '''Kulminationspunkte''' auf dem südlichen Meridian und die '''Monduntergangspunkte''' am westlichen Horizont für die vier verschiedenen Jahreszeiten angegeben:
{| class="wikitable"
|+ Die Lage des Bogens der Ekliptik über dem nächtlichen Horizont zu verschiedenen Jahreszeiten
!title="Jahreszeit"| Jahreszeit
!title="Am Abend"| Am Abend</br>(Aufgang des Vollmonds</br>in östlicher Richtung)
!title="Um Mitternacht"| Um Mitternacht</br>(Kulmination des Vollmonds</br>auf dem südlichen Meridian)
!title="Am Morgen"| Am Morgen</br>(Untergang des Vollmonds</br>in westlicher Richtung)
|-
| '''Frühlingsanfang'''</br>'''Tag-und-Nacht-Gleiche''' || [[Datei:Ekliptik.C.png|360px]] || [[Datei:Ekliptik.D.png|360px]] || [[Datei:Ekliptik.A.png|360px]]
|-
| '''Sommeranfang'''</br>'''Sommersonnenwende''' || [[Datei:Ekliptik.D.png|360px]] || [[Datei:Ekliptik.A.png|360px]] || [[Datei:Ekliptik.B.png|360px]]
|-
| '''Herbstanfang'''</br>'''Tag-und-Nacht-Gleiche''' || [[Datei:Ekliptik.A.png|360px]] || [[Datei:Ekliptik.B.png|360px]] || [[Datei:Ekliptik.C.png|360px]]
|-
| '''Winteranfang'''</br>'''Wintersonnenwende''' || [[Datei:Ekliptik.B.png|360px]] || [[Datei:Ekliptik.C.png|360px]] || [[Datei:Ekliptik.D.png|360px]]
|}
[[Datei:Orbit de Mondknoten.svg|mini|rechts|hochkant=2|Bahnellipse des Mondes mit den beiden Mondknoten.]]
Im Laufe eines '''drakonitischen Monats''' geht der Mond sowohl durch den aufsteigenden als auch durch den absteigenden Knoten (Drachenpunkte) seiner Mondbahn. Nach dem Durchqueren des aufsteigenden Knotens wird die ekliptikale Breite des Mondes positiv (nördliche ekliptikale Breite), er steigt in Bezug zur Ekliptiklinie nach Norden und bleibt entsprechend immer länger über dem Horizont. Nach dem Durchqueren des absteigenden Knotens wird die ekliptikale Breite des Mondes negativ (südliche ekliptikale Breite), er fällt in Bezug zur Ekliptiklinie nach Süden und bleibt entsprechend immer kürzer über dem Horizont. In der deutschsprachigen Schweiz gibt es für diese im Laufe eines drakonitischen Monats täglich mehr oder weniger deutlich wahrnehmbaren Änderungen der ekliptikalen Breite sogar eigene Adjektive. Das Ansteigen der ekliptikalen Breite des Mondes nach Norden wird '''obsigend''', und das Abfallen der ekliptikalen Breite des Mondes nach Süden wird '''nidsigend''' genannt.
[[Datei:Mond-.und.Sonnenwenden.png|mini|rechts|hochkant=2|Verschiedene Mond- und Sonnenwenden auf dem südlichen Meridian im Laufe eines Jahres in Bezug zum Horizontsystem.]]
Im Laufe der Zeit pendelt der Mond immer wieder zwischen den kleinen und großen Mondwenden. Dieser Ablauf wiederholt sich nach einem vollständigen '''drakonitischen Zyklus''', also nach 18,61 Jahren, innerhalb dessen die kleinste und die größte Mondwende im zeitlichen Abstand von rund 9,3 Jahren jeweils einmal erreicht wird.
Die Deklinationen der Sonne <math>\delta_S</math> pendeln also je nach Jahreszeit maximal um den Betrag der Schiefe der Ekliptik <math>\varepsilon</math> um den Nullpunkt <math>\delta_S = 0</math>, der nur zu den Tag-und-Nacht-Gleichen erreicht wird:
:<math>-\varepsilon \le \delta_S \le +\varepsilon</math>
Die Deklinationen des Mondes <math>\delta_M</math> pendeln zusätzlich um den Betrag der Inklination der Mondbahn <math>i</math> (heute '''5,145 Bogengrad''') um die Werte der Deklination der Sonne <math>\delta_S</math>:
:<math>-\varepsilon - i \le \delta_M \le +\varepsilon + i</math>
Die Deklination und der maximale Höhenwinkel bei der Kulmination des Mondes auf dem südlichen Meridian variieren also sowohl innerhalb eines Monats durch die Inklination der Mondbahn als auch jährlich durch die Neigung der Erdachse (also die Schiefe der Ekliptik).
Die für die Azimute der Mondaufgänge <math>a_{A,M}</math> anzuwendende Formel hängt nicht nur von der augenblicklichen Deklination des Mondes <math>\delta_M</math>, sondern auch vom geographischen Breitengrad des Beobachters <math>\varphi</math> ab:
:<math>\cos a_{A,M} = \frac {\sin \delta_M} {\cos \varphi}</math>
Aus Symmetriegründen ergibt sich für die Azimute der Monduntergänge <math>a_{U,M}</math>:
:<math>a_{U,M} = 360^\circ - a_{A,M} = 360^\circ - \arccos \frac {\sin \delta_M} {\cos \varphi}</math>
{| class="wikitable"
|+ Deklinationen des Mondes
! Ereignis !! Deklination des Mondes <math>\delta_M</math> !! Maximaler Höhenwinkel des Mondes<br/><math>h_{max,M}</math> || Azimut des Mondaufgangs<br/><math>a_{A,M}</math> || Azimut des Monduntergangs<br/><math>a_{U,M}</math>
|-
| Kleinste Mondwende<br/><math>\delta_S = -\varepsilon</math> || <math>-(\varepsilon + i)</math> || <math>(90^\circ - \varphi) - (\varepsilon + i)</math> || <math>\arccos \frac {\sin (-\varepsilon - i)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (-\varepsilon - i)} {\cos \varphi}</math>
|-
| Kleine Mondwende || <math>\delta_S - i</math> || <math>(90^\circ - \varphi) + (\delta_S - i)</math> || <math>\arccos \frac {\sin (\delta_S - i)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (\delta_S - i)} {\cos \varphi}</math>
|-
| Mond im Knoten || <math>\delta_S</math> || <math>(90^\circ - \varphi) + \delta_S</math> || <math>\arccos \frac {\sin (\delta_S)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (\delta_S)} {\cos \varphi}</math>
|-
| Große Mondwende || <math>\delta_S + i</math> || <math>(90^\circ - \varphi) + (\delta_S + i)</math> || <math>\arccos \frac {\sin (\delta_S + i)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (\delta_S + i)} {\cos \varphi}</math>
|-
| Größte Mondwende<br/><math>\delta_S = +\varepsilon</math> || <math>+(\varepsilon + i)</math> || <math>(90^\circ - \varphi) + (\varepsilon + i)</math> || <math>\arccos \frac {\sin (\varepsilon + i)} {\cos \varphi}</math> || <math>360^\circ - \arccos \frac {\sin (\varepsilon + i)} {\cos \varphi}</math>
|}
Die Höhenwinkel des Mondes und der Sonne konnten zum Beispiel schon in der Bronzezeit mit Hilfe von '''Sonnenuhren''', '''Stabdolchen''' oder '''Kreisgrabenanlagen''' beobachtet und bestimmt werden. Im Laufe eines Jahres konnten damit die jahreszeitlichen Veränderungen der scheinbaren Sonnenbahn verfolgt werden. Durch eine langjährige Beobachtung war es dann auch möglich, die kleinsten und größten Mondwenden zu ermitteln.
Siehe hierzu auch:
* → '''[[Quadriviale_Kuriositäten/_Stabdolche|Stabdolche]]'''
* → '''[[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Kalenderführung#Frühgeschichtliche_Observatorien|Frühgeschichtliche Observatorien]]'''
===Vergleich der Perioden===
Die folgenden Periodendauern spielen bei Sonnen- und Mondzyklen eine besondere Rolle:
* '''Meton-Zyklus:'''
** 19 '''tropische Sonnenjahre''' = 6939,6 Tage, bestimmt durch die ekliptikale Länge der Sonne in Bezug zum Frühlingspunkt mit ihren Äquinoktien und Sonnenwenden.
** 235 '''synodische Monate''' = 6939,7 Tage, bestimmt durch die Mondphasen mit Neumonden und Vollmonden.
** 254 '''siderische Monate''' = 6939,7 Tage, bestimmt durch die ekliptikale Länge des Mondes in Bezug zum Frühlingspunkt und den Fixsternhimmel.
** 255 '''drakonitische Monate''' = 6939,1 Tage, bestimmt durch die ekliptikale Breite des Mondes beziehungsweise die auf- und absteigenden Knoten der Mondbahn.
* '''Drakonitischer Zyklus:'''
** 249,83 '''drakonitische Monate''' = 6798,38 Tage = 18,613 tropische Sonnenjahre, bestimmt durch die ekliptikalen Längen der auf- und absteigenden Knoten der Mondbahn: Zyklus der Mondwenden und Sternbedeckungen (Nutationsperiode)
* '''Saros-Zyklus:'''
** 242 '''drakonitische Monate''' = 6585,3 Tage = 18,03 tropische Sonnenjahre (respektive 18 Jahre und 11 Tage): Zyklen der Sonnen- und Mondfinsternisse
** 223 '''synodische Monate''' = 6585,4 Tage, bestimmt durch die Mondphasen mit Neumonden und Vollmonden.
[[Datei:Meton-Zyklus.2023.4x5.png|mini|links|hochkant=4|Der 19-jährige Meton-Zyklus vom Januar 2023 bis zum Januar 2042. Die Zeit schreitet in horizontaler Richtung jeweils um einen halben Tag pro Bildpunkt voran.<br/>Im unteren Teil des Diagramms sind die kleinen und großen Mondwenden für die geographische Breite 52,5 Bogengrad (zum Beispiel in Berlin) dargestellt.]]
<div style="clear:both"></div>
[[Datei:Herbstaequinoktium.kleine.Mondwende.2033.png|mini|rechts|hochkant=1|Herbstäquinoktium (orange) und Sonnenfinsternis bei aufsteigendem (rot) Neumond am 23. September 2033 (orangefarbene Linie), Mondfinsternis bei absteigendem (blau) Vollmond am 8. Oktober 2033 und Wintersonnenwende (türkis) mit kleiner Mondwende bei Neumond am 21. Dezember 2033 (grüne Linie).]]
Der '''Vollmond''' (weiß) erscheint sowohl zu Beginn der Darstellung am 6. Januar 2023 als auch nach genau 19 Jahren am 6. Januar 2042. Dieser Sachverhalt ergibt sich daraus, dass nach dem Durchlauf eines Meton-Zyklus eine ganzzahlige Anzahl von 235 synodischen Perioden durchlaufen wird (gleiche '''Elongation''' des Mondes). Diese Vollmonde stehen bei der gleichen ekliptikalen Länge im Sternbild Zwillinge (Gemini). Dieser Sachverhalt ergibt sich daraus, dass nach dem Durchlauf eines Meton-Zyklus eine ganzzahlige Anzahl von 254 siderischen Perioden durchlaufen wird. Gleichzeitig steht auch die Sonne nach 19 tropischen Sonnenjahren auf dem Zodiak bei der gleichen ekliptikalen Länge und somit erneut genau gegenüber vom Mond im Sternbild Schütze (Sagittarius). Da die Meton-Periode nur gut einen halben Tag länger ist als 255 drakonitische Perioden, stehen diese beiden Vollmonde bei fast der gleichen ekliptikalen Breite nördlich vom Stern Mebsuta (ε Geminorum).
'''Große Mondwenden''' mit maximaler Höhe des Mondes und längstem Bogen über dem Horizont folgen stets dem aufsteigenden Mondknoten (rot), und '''kleine Mondwenden''' mit minimaler Höhe des Mondes und kürzestem Bogen über dem Horizont folgen stets dem absteigenden Mondknoten (blau).
[[Datei:KleinsteMondwende.23.6.2024.HDR.png|mini|hochkant=2|'''Kleinste Mondwende um die Mittsommernacht 2024''': Der Vollmond (scheinbarer Durchmesser = 31,8 Minuten) am 23. Juni 2024 nach ein Uhr und kurz vor der oberen Kulmination auf dem südlichen Meridian von Berlin-Lankwitz (nördliche geographische Breite = 52,44 Bogengrad) aus gesehen. Die ekliptikale Breite hatte einen Minimalwert, der fast 6 Bogengrad südliche Breite betrug und somit zu einer extrem geringen maximalen Höhe über dem Horizont von nur 8,5 Bogengrad führte. Im norwegischen Trondheim (nördliche geographische Breite = 63,44 Bogengrad, also drei Breitengrade südlich vom nördlichen Polarkreis) oder im isländischen Reykjavik (nördliche geographische Breite = 64,15 Bogengrad) war in diesen Tagen weder die Mitternachtssonne noch der Vollmond zu sehen. Der helle Stern rechts oberhalb der Mondscheibe ist Nunki im Schützen (σ Sagittarius, visuell 2,7<sup>m</sup>), unterhalb ist Asella (ζ Sagittarius, visuell 4,2<sup>m</sup>) zu sehen.]]
Die '''größte Mondwende''' bei Vollmond tritt nur zur Wintersonnenwende und die '''kleinste Mondwende''' bei Vollmond tritt nur zur Sommersonnenwende auf. Je nach ekliptikaler Breite des Mondes schwanken die absoluten Werte für die Horizontwinkel um das doppelte der Inklination der Mondbahn, das heißt insgesamt um bis zu 10,29 Bogengrad. Die ekliptikale Breite bezieht sich auf den Erdmittelpunkt, da dieser in der Ekliptikebene liegt. Die von der Erdoberfläche aus beobachtete ekliptikale Breite des im Mittel nur zirka 383100 Kilometer entfernten Mondes hängt zusätzlich vom jeweiligen geographischen Breitengrad der Beobachtung ab. Der in Bezug auf die Ekliptik nördlichste und südlichste Punkt auf der Erdoberfläche liegen um den Erdradius von gut 6350 Kilometer oberhalb (nördlich) beziehungsweise unterhalb (südlich) der Ekliptikebene. Daraus resultiert für die beobachtete ekliptikale Breite ein zusätzlicher Winkel von knapp einem Bogengrad:
:<math>\Delta \beta = \arctan \frac {\pm 6350} {383100} \approx \pm 0,95^\circ</math>
Die '''größte Mondwende bei Vollmond''' (weiß) tritt bei maximaler ekliptikaler Breite des Mondes um die Wintersonnenwende (türkis, siehe Dezember 2023) auf. Die '''kleinste Mondwende bei Vollmond''' (weiß) tritt bei minimaler ekliptikaler Breite des Mondes um die Sommersonnenwende (gelb, siehe Juni 2024).
Im 19-jährigen Diagramm (siehe oben) ist gut zu erkennen, wie sich die Konstellation zwischen Mondknoten (rot und blau) und Mondphasen (weiß und schwarz) nach den 19 Jahren des '''Meton-Zyklus''' wiederholt (vergleiche Januar 2023 und Januar 2042).
Der erste aufsteigende Mondknoten (rot) wird fünf Tage vor Vollmond am 2. Januar 2023 erreicht. Nach einer vollständigen '''Saros-Periode''' von 18 Jahren und 11 Tagen, also am 12. Januar 2041 wird fünf Tage vor Vollmond ebenfalls wieder ein aufsteigender Mondknoten erreicht.
Eine Besonderheit gibt es im März 2038: Die Tag-und-Nacht-Gleiche erfolgt bereits am 20. März 2038 (Samstag) und der erste Frühlingsvollmond nur einige Stunden später am 21. März 2038 (Sonntag). Grundsätzlich müsste also der darauffolgende Sonntag, nämlich der 28. März 2038, das '''Osterdatum''' sein. In diesem Fall greift die abweichende Regel, dass erst der Vollmond nach dem 21. März als Frühlingsvollmond zu rechnen ist, was erst am 19. April 2038 (Montag) der Fall ist. Demzufolge ist das Osterdatum auf den 25. April 2038 (Sonntag) festgelegt.
Eine '''Sonnenfinsternis '''tritt immer dann auf, wenn ein Neumond (weiß) zeitnah durch einen aufsteigenden (rot) oder absteigenden (blau) Mondknoten läuft. Dies ist im obigen Diagramm an den folgenden Tagen der Fall:
* 20. April 2023, 14. Oktober 2023, 8. April 2024, 2. Oktober 2024, 29. März 2025, '''21. September 2025''' (Herbstäquinoktium), 17. Februar 2026, 12. August 2026, 6. Februar 2027, 2. August 2027, 26. Januar 2028, 22. Juli 2028, 14. Januar 2029, 12. Juni 2029, 11. Juli 2029, 5. Dezember 2029, 1. Juni 2030, 25. November 2030, 21. Mai 2031, 14. November 2031, 9. Mai 2032, 3. November 2032, 30. März 2033, '''23. September 2033''' (Herbstäquinoktium), '''20. März 2034''' (Frühlingsäquinoktium), 12. September 2034, 9. März 2035, 2. September 2035, 27. Februar 2036, 23. Juli 2036, 21. August 2036, 16. Januar 2037, 13. Juli 2037, 5. Januar 2038, 2. Juli 2038, 26. Dezember 2038, '''21. Juni 2039''' (Sommersonnenwende), 15. Dezember 2039, 11. Mai 2040, 4. November 2040, 30. April 2041, 25. Oktober 2041
Genau neunzehn Jahre nach den ersten beiden genannten Sonnenfinsternissen im Jahr 2023 treten an den gleichen Kalendertagen erneut zwei Sonnenfinsternisse auf, nämlich am 20. April 2042 und am 14. Oktober 2042.
Neunzehn Jahre vor den sechs ersten aufgeführten Sonnenfinsternissen in den Jahren 2023 bis 2025 traten an fast immer den gleichen Kalendertagen ebenfalls Sonnenfinsternisse auf, nämlich am 19. April 2004, am 14. Oktober 2004, am 8. April 2005, am 3. Oktober 2005, am 29. März 2006 und am 22. September 2006.
Eine '''Mondfinsternis '''tritt immer dann auf, wenn ein Vollmond (weiß) zeitnah durch einen aufsteigenden (rot) oder absteigenden (blau) Mondknoten läuft. Dies ist im obigen Diagramm an den folgenden Tagen der Fall:
* 5. Mai 2023, 28. Oktober 2023, 25. März 2024, 18. September 2024, 14. März 2025, 7. September 2025, 3. März 2026, 28. August 2026, 20. Februar 2027, 18. Juli 2027, 17. August 2027, 12. Januar 2028, 6. Juli 2028, 31. Dezember 2028, 26. Juni 2029, '''20. Dezember 2029''' (Wintersonnenwende), 15. Juni 2030, 9. Dezember 2030, 7. Mai 2031, 5. Juni 2031, 30. Oktober 2031, 25. April 2032, 18. Oktober 2032, 14. April 2033, 8. Oktober 2033, 3. April 2034, 28. September 2034, 22. Februar 2035, 19. August 2035, 11. Februar 2036, 7. August 2036, 31. Januar 2037, 27. Juli 2037, 21. Januar 2038, '''17. Juni 2038''' (Sommersonnenwende), 16. Juli 2038, 11. Dezember 2038, 6. Juni 2039, 30. November 2039, 26. Mai 2040, 18. November 2040, 16. Mai 2041, 8. November 2041
<gallery caption="Finsternisse" perrow="1" widths="1024" heights="470">
Finsternisse.2025.png|Die Finsternisse im Jahr 2025 liegen in der Nähe der Tag-und-Nacht-Gleichen, wo mittlere Mondwenden vorliegen. Der Mond geht im Osten auf und im Westen unter.
Finsternisse.2029.png|Die Finsternisse im Jahr 2029 liegen in der Nähe der Sonnenwenden. Bei der Sonnenfinsternis im Juni kommt es bei Neumond zu einer großen Mondwende, und bei der darauffolgenden Mondfinsternis kommt es bei Vollmond zu einer kleinen Mondwende. Bei der Sonnenfinsternis im Dezember kommt es bei Neumond zu einer kleinen Mondwende, und bei der darauffolgenden Mondfinsternis kommt es bei Vollmond zu einer großen Mondwende.
</gallery>
==Der Kalenderstein vom Tempel Mnajdra==
Auf Malta wurde im Hypogäum von Ħal-Saflieni beim Ort Tarxien ein annähernd kreisrunder Stein aus der Tempelperiode der Insel mit zirka sechs Zentimeter Durchmesser gefunden, der wie die Darstellung einer Vollmondscheibe aussieht.<ref>Daniel Cilia: [http://web.infinito.it/utenti/m/malta_mega_temples/TempleFig/%20Pres,Misc/pages/face.htm Found in a house at Hal Saflieni, stone, c.6 cm wide], The megalithic temples of Malta - the world's most ancient stone architectur, DSCF9754</ref> Im maltesischen Tempel Mnajdra sind an der südlichen Küste Maltas zirka zehn Kilometer entfernt davon zwei große Kalendersteine gefunden worden, die ebenfalls aus dieser Zeit stammen.
<gallery caption="Die Ruinen der Tempelanlage von Mnajdra" widths="300" heights="300" mode="packed">
Plan_der_Tempel_von_Mnajdra.png|Plan der Tempelanlage. Die beiden Kalendersteine befinden sich in der Mitte des Urtempels im Osten der Anlage (Buchstabe "A"), die aus dem vierten vorchristlichen Jahrtausend stammt.
Mini_Europa_Brüssel-2125_Tempel_von_Mnajdra.jpg|Modell der Tempelanlage im Park "Mini-Europa" in Brüssel. Die Kalendersteine befinden sich links und rechts hinter der Frauenfigur auf der rechten Seite.
Malta_-_Qrendi_-_Hagar_Qim_and_Mnajdra_Archaeological_Park_-_Mnajdra_16_ies.jpg|Darstellung der Lochreihen der beiden Kalendersteine auf einer Schautafel der archäologischen Stätte.
Mnajdra_East_Temple_1_(6799974274).jpg|Blick vom Eingang des Osttempels auf die beiden aufrechtstehenden Kalendersteine – links der westliche und rechts der östliche Kalenderstein.
Mnajdra_Temple_-_Chris_Brown.jpg|Blick über die Kalendersteine – links der östliche und rechts der westliche Kalenderstein, den Eingang des Osttempels und die Südküste Maltas auf das Mittelmeer mit der vorgelagerten Insel Filfla.
</gallery>
Auf dem östlichen Kalenderstein gibt es mehrere Lochreihen, deren Lochzahlen alle mit lunaren und solaren Kalendern in Zusammenhang gebracht werden können. In der heute zusammengestellten Anordnung der Steine können nicht alle Löcher gebohrt worden sein, da diese teilweise von anderen Steinen verdeckt sind. Die Bohrungen sind heute in horizontaler Richtung ausgerichtet, wurden damals vermutlich unter Ausnutzung der Gravitation senkrecht nach unten auf dem noch liegenden Stein ausgeführt. In dieser Ausrichtung des Steins wäre es auch leicht möglich gewesen, für Markierungs- oder Zählzwecke zum Beispiel kugelförmige Gegenstände in die Löcher zu legen. Steckkalender aus Stein mit Bohrungen (bei den Griechen hießen sie '''Parapegmata''') waren im Altertum an vielen Orten in Verwendung.
Am Kopf des Steins gibt es mehrere hundert, flächenhaft angeordnete Löcher, die eventuell für die einzelnen Monate oder Jahre einer langfristigen Beobachtung stehen. Darunter tauchen rechtsbündig sieben horizontale Lochreihen auf, die in der Skizze mit den Buchstaben A bis G gekennzeichnet sind, wobei die beiden Teilreihengruppen B1 und B2 sowie C1, C2 und C3 zusammengefasst betrachtet werden:
[[Datei:Kalenderstein.Mnajdra.labelled.png|mini|links|hochkant=3|Skizze der Lochreihen auf dem Kalenderstein von Mnajdra nach Ventura und Hoskin.<ref name="Ventura">Frank Ventura, Michael Hoskin: [[doi:10.1007/978-1-4614-6141-8_133|Temples of Malta]], in: Clive Ruggles (Herausgeber), ''Handbook of Archaeoastronomy and Ethnoastronomy'', 7. Juli 2014, Seiten 1421-1430, Springer, New York, ISBN 978-1-4614-6140-1</ref>]]
{| class="wikitable"
|+ Lochreihen auf dem östlichen Kalenderstein vom Tempel Mnajdra auf Malta
!title="Reihe"| Reihe
!title="Anzahl der Löcher"| Anzahl der Löcher
!title="Mögliche Verwendung"| Mögliche Verwendung
|-
| A || 19 || Für die jeweilige Goldene Zahl jedes Sonnenjahres innerhalb des 19-jährigen '''Meton-Zyklus''' (235 synodische, 255 drakonitische, 254 siderische Monate beziehungsweise 6940 Tage).<br/>Nach einem Sonnenjahr hat die Sonne wieder die gleiche ekliptikalen Länge. Nach Ablauf der gesamten Meton-Periode hat der Mond wieder die gleiche Mondphase '''und''' die gleiche ekliptikalen Breite '''und''' die gleiche ekliptikalen Länge (zum Beispiel im Goldenen Tor der Ekliptik oder im Frühlingspunkt).
|-
| rowspan=2 | B || B<sub>1</sub>: 13 (links) || rowspan=2 | In Summe 29, für die Anzahl der vollständigen Tage in einem '''synodischen Monat''' (29,5 Tage). Nach dieser Zeit hat der Mond wieder die gleiche Mondphase erreicht.<br/>Vom Altlicht des Mondes bis zum Vollmond sind es 16 Tage, und danach sind es 13 Tage bis zum nächsten Altlicht.<br/>Nachdem die Doppelreihe vervollständigt wurde, gibt es dafür einen Übertrag in die Reihe E und wenn diese bereits voll ist, für das nächste beginnende Jahr mit der nächstfolgenden '''Goldenen Zahl''' einen Übertrag in die Reihe A.
|-
| B<sub>2</sub>: 16 (rechts darunter)
|-
| rowspan=3 | C || C<sub>1</sub>: 3 (rechts oben) || rowspan=2 | Für die sieben vollständigen Tage eines '''Mondviertels''' (≈7,4 Tage) respektive einer '''Woche'''.<br/>Wenn diese Doppelreihe gefüllt ist, gibt es für die Vervollständigung einer neuen Woche einen Übertrag in die Reihe G für die Wochen in einem Jahr.<br/>Alternativ könnten hier jeweils die drei Monate in den vier Jahreszeiten markiert und gezählt worden sein.<br/>Ein weiterer Zusammenhang besteht eventuell mit der Tatsache, dass in 19 Jahren (siehe Reihe A) sieben synodische Schaltmonate erforderlich sind, um in einem lunisolaren Kalender den Lauf von Sonne und Mond bis auf wenige Stunden genau zu synchronisieren (siehe hierzu auch''' [[Astronomie_von_der_Frühgeschichte_bis_zur_Neuzeit/_Die_Plejaden#Schaltregeln|Plejaden-Schaltregeln]]''').
|-
| C<sub>2</sub>: 4 (rechts unten)
|-
| C<sub>3</sub>: 3 (links) || Für die drei nach Neumond '''vollendeten Mondviertel''' innerhalb eines laufenden synodischen Monats.</br>Beim Erreichen eines Neumonds, eines abnehmenden Halbmonds, eines Vollmonds oder eines abnehmenden Halbmonds gibt es jeweils einen Übertrag in die Reihe D oder in die Reihe F (siehe unten).
|-
| D || 25 || Für die 25 '''vollendeten Mondviertel''' in der ersten Hälfte eines Sonnenjahres, oder entweder für alle '''zunehmenden Mondviertel''' (wenn der Mond zu Beginn des Jahres zunehmend war) oder für alle '''abnehmenden Mondviertel''' (wenn der Mond zu Beginn des Jahres abnehmend war) eines Sonnenjahres (vergleiche Reihe F).
|-
| E || 11 || Nachdem die Doppelreihe B vollständig durchlaufen wurde, gibt es einen Übertrag in diese Reihe. Diese elf Mulden stehen dann für '''überzähligen Tage''' in einem Sonnenjahr (365,2 Tage) im Vergleich zu zwölf synodischen Monaten (354,4 Tage). Wenn diese Reihe bereits voll ist, gibt es für das nächste beginnende Jahr mit der nächstfolgenden '''Goldenen Zahl''' einen Übertrag in die Reihe A.
|-
| F || 24 + 1 = 25 || Für die 24 bis 25 '''vollendeten Mondviertel''' in der zweiten Hälfte eines Sonnenjahres, oder entweder für alle '''abnehmenden Mondviertel''' (wenn der Mond zu Beginn des Jahres zunehmend war) oder für alle '''zunehmenden Mondviertel''' (wenn der Mond zu Beginn des Jahres abnehmend war) eines Sonnenjahres (vergleiche Reihe D). Das 25. Loch ist etwas abgesetzt, da es für ein am Ende des Jahres eingeschaltetes 50. Mondviertel (Dauer = 7,38265 Tage) steht, das nur in ungefähr jedem zweiten Sonnenjahr auftritt:<br/>
:49 x 7,38265 Tage ≈ 361,75 Tage beziehungsweise 50 x 7,38265 Tage ≈ 369,13 Tage<br/>
:365,242 Tage - 361,75 Tage ≈ 3,5 Tage beziehungsweise 365,242 Tage - 369,13 Tage ≈ -3,9 Tage
|-
| G || 53 || Für die begonnenen 53 '''Siebentagewochen''' in einem Sonnenjahr (Dauer = 365,242 Tage) beziehungsweise von einem heliakischen Auf- oder akronychischen Untergang der Plejaden zum nächsten.
|}
[[Datei:Altlicht.16.9.2020.P1079087.jpg|mini|rechts|Über dem östlichen Horizont beim Morgenletzt gerade noch sichtbares Altlicht des abnehmenden Mondes 33 Stunden vor Neumond mit der vom Erdschein beleuchteten Nachtseite des Mondes. Die Aufnahme entstand kurz vor Herbstbeginn, als die Ekliptik morgens fast senkrecht auf dem Horizont stand.]]
Zu der Doppelreihe B sei angemerkt, dass auch im altägyptischen Mondkalender, der im Neolithikum in Verwendung war, der Monat nicht mit dem unsichtbaren Neumond, sondern mit dem gerade noch sichtbaren Altlicht des Morgenletztes des Mondes begann, also gut einen Tag vor Neumond.<ref>Joachim Friedrich Quack: [https://core.ac.uk/download/pdf/35120251.pdf Zwischen Sonne und Mond - Zeitrechnung im Alten Ägypten], Seite 38, in: Harry Falk (Herausgeber), ''Vom Herrscher zur Dynastie. Zum Wesen kontinuierlicher Zeitrechnung in Antike und Gegenwart'', Bremen 2002</ref> Die beiden letzten Löcher sind etwas nach links abgesetzt, was mit folgendem Sachverhalt im Einklang steht: zwei Tage vor dem Ende einer synodischen Periode, also schon nach gut 27 Tagen, ist ein siderischer Monat vorüber, nach welchem der Mond die gleiche ekliptikale Länge erreicht hat. Das heißt bereits nach gut 27 Tagen steht der Mond zum Beispiel wieder im Goldenen Tor der Ekliptik, bevor er erst nach gut 29 Tagen erneut sein Altlicht erreicht (gut einen Tag vor Neumond). Die Sonne ist innerhalb des synodischen Monats durch die Bewegung der Erde um die Sonne gegenüber dem Fixsternhimmel um knapp 30 Bogengrad weiter nach links gezogen.
Die 25 Wochen des abnehmenden Mondes werden zum Beispiel auch im 79. Kapitel des ungefähr 2000 Jahre alten {{w|Äthiopisches Henochbuch|äthiopischen Henochbuches}} erwähnt.<ref>Siehe auch {{s|Henochbuch_(oder_Erster_Henoch)#Der_dritte_Teil_72–82_Das_astronomische_Buch|Wikisource, Henochbuch, Teil 3, Kapitel 71 bis 82}}</ref> Alternativ könnten die 50 Löcher in Reihen D und F eventuell auch für die 50 vollständigen Siebentagewochen (350 Tage) innerhalb von zwölf synodischen Perioden stehen, die eine Dauer von 50,6 Wochen beziehungsweise 354,4 Tagen haben.
Zur Zahl Elf (Reihe E) ist noch festzuhalten, dass die Erde innerhalb eines siderischen Jahres des Planeten Jupiter (zwölf Erdenjahre) elf Mal mit diesem in Opposition steht. Zu diesen Zeitpunkten ist der Abstand zwischen Erde und Jupiter am geringsten, der Jupiter hat steht in seinem größten Glanz und er kulminiert um Mitternacht auf dem südlichen Meridian.
[[Datei:Mnajdra East Temple 2 (6946102161).jpg|rechts|mini|Detail des westlichen Kalendersteins aus dem Osttempel von Mnajdra.]]
Auch auf einem weiteren, sogenannten westlichen und heute ebenfalls aufgerichteten Stein der Tempelanlage sind mehrere Lochreihen zu sehen, die aus 16, 12, 19, 7, 30, 31, 32, 35, 37, 12 und 13 Löchern bestehen.<ref>David Humiston Kelley, Eugene Frank Milone: ''Exploring Ancient Skies: A Survey of Ancient and Cultural Astronomy'', Part II ''Astronomy in Cultures'', 6 ''Paleolithic and Neolithic Cultures'', 6.2 ''Megalithic Cultures'', 6.2.18 ''Mediterranean and North African Megalithic Sites'', 6.2.18.1 ''Malta'', pages 201 and 202, Springer, 2011, ISBN 9781441976246</ref> Einige dieser Zahlen tauchen auch im Zusammenhang mit dem östlichen Stein auf oder sind ebenfalls leicht mit lunaren oder solaren Kalendertagen in Verbindung zu bringen:
* 16: Anzahl der Tage vom Altlicht bis zum Vollmond.
* 12: Anzahl der vollständigen Monate pro Jahr.
* 19: Anzahl der tropischen Jahre pro Meton-Periode.
* 7: Anzahl der vollständigen Tage pro Mondviertel respektive der Tage pro Woche.
* 30: aufgerundete Anzahl der Tage pro synodische Periode respektive der Tage pro Monat.
→ In Bezug auf die Bedeutung von bestimmten Zahlen in der Astronomie siehe auch '''[[Quadriviale Kuriositäten/ Zahlen|Exkurs „Zahlen“]]'''.
<div style="clear:both"></div>
==Malereien in der Höhle von Magura==
In der schon im Neolithikum genutzten Magura-Höhle im Nordwesten des heutigen Bulgariens gibt es nicht nur eine sehr alte bildliche Darstellung eines Schöpfungsmythos, sondern ebenfalls Hinweise darauf, dass verschiedene Mondzyklen bekannt waren. Unter den Darstellungen befindet sich insbesondere eine Reihe von Strichen, mit denen die 16 Tage vom Altlicht des Mondes bis zu Vollmond gezählt worden sein können.
→ Weitere Erläuterungen finden sich im '''[[Die_Höhlenmalerei_in_der_Magura-Höhle|Wikibook „Die Höhlenmalerei in der Magura-Höhle“]]'''.
[[Datei:Magura.Altlicht.Vollmond.png|links|mini|hochkant=4|Erste Hälfte des synodischen Monats in einer Darstellung der Höhlenmalerei von Magura. Rechts ist die schmale, liegende Mondsichel des Altlichts beim Morgenletzt zu sehen. Ein bis zwei Tage später ist Neumond, danach nimmt der Mond wieder zu, und nach insgesamt sechzehn Tagen wird der Vollmond erreicht (links).]]
<div style="clear:both"></div>
== Kontrastverhältnisse bei aschgrauem Mondlicht ==
[[Datei:Aschgraues.Mondlicht.P1162513.jpg|mini|hochkant=2|Die vom Erdschein beleuchtete Nachtseite des Mondes wird neben der schmalen Mondsichel (11 Prozent, Mondalter = 3,3 Tage) mit ''aschgrauem Mondlicht'' sichtbar. Aufnahme über dem westlichen Abendhimmel kurz nach dem Äquinoktium im Frühjahr bei besonders steiler Ekliptik von Berlin aus gesehen. Links der Stern Omikron Arietis (37 Ari, 6,2<sup>m</sup>) im Sternbild Widder.<br/><br/>
Mit bloßen Augen ist das aschgraue Licht im Mondschatten nur unter sehr guten atmosphärischen Bedingungen und mit voller menschlicher Sehkraft gerade noch erkennbar. Der Mond hat nur einen Winkeldurchmesser von 0,5 Bogengrad, was bei ausgestrecktem Arm nur ungefähr einem Viertel der Breite eines Fingers entspricht. Die hier gezeigte Aufnahme ist sehr hell belichtet, so dass die Kontraste im Mondschatten einfacher wahrzunehmen sind als in der Natur:
[[Datei:Aschgraues.Mondlicht.P1162513.jpg|90px|zentriert]]
]]
Das '''aschgraue Mondlicht''' kann wenige Tage vor und nach Neumond im Schattenbereich des Mondes beobachtet werden, wenn die Sonne unter dem Horizont steht. Der Schattenbereich wird hierbei durch den '''Erdschein''' beleuchtet und hebt sich deutlich sichtbar vom Himmel ab, der den Mond umgibt. Der Erdschein hängt vom Albedo der vom direkten Sonnenlicht getroffenen Erde ab und kann insbesondere durch den Anteil an Wolken in der Atmosphäre sowie an Eis- und Schneeflächen auf der Erdoberfläche schwanken.
=== Historische Beobachtungen ===
Bereits die Babylonier haben im sechsten vorchristlichen Jahrhundert diesen visuellen Effekt in bildlichen Darstellungen auf Steinreliefs mit dem König Nabonid vor der Mondsichel mit aschgrauem Licht festgehalten:<ref>Friedhelm Pedde: ''[https://wfs.berlin/wp-content/uploads/2021/09/BROplanet-12-okt2021ff_www.pdf Götter und Planeten im Alten Orient - Sin und der Mond]'', Mitteilungen, Ausgabe 12, Seite 6 und 7, Oktober 2021, Wilhelm-Foerster-Sternwarte e.V. / Zeiss-Planetarium am Insulaner</ref>
<gallery caption="" mode="packed" widths="600" heights="400">
Urfa museum King Nabonid embossed inscription Iron age - 5026.jpg|Darstellung des letzten Königs des Neubabylonischen Reiches, Nabonid, vor der Mondsichel mit aschgrauem Mondlicht am Kopf einer babylonischen Stele aus Harran im Archäologischen Museum Şanlıurfa in der Türkei aus dem sechsten vorchristlichen Jahrhundert.
Nabonidus.jpg|Darstellung des letzten Königs des Neubabylonischen Reiches, Nabonid, vor der Mondsichel mit aschgrauem Mondlicht am Kopf einer babylonischen Stele im Britischen Museum.
Voynich.68r.Mondsichel.Erdschein.png|Darstellung der Mondsichel mit einem Gesicht im Schattenbereich des Mondes im astronomischen Abschnitt des Voynich-Manuskripts aus dem 15. Jahrhundert.
Caspar David Friedrich - Zwei Männer in Betrachtung des Mondes.jpg|Zwei Männer am Abend bei der Betrachtung des Neulichts von Caspar David Friedrich (1819/1820), Öl auf Leinwand, Albertinum, Dresden. Eine in dieser Darstellung ähnliche Konstellation zwischen einer solchen Mondsichel und dem Planeten Jupiter fand am Abend des 28. Märzes 1811 im Sternbild Stier (Taurus) statt. Eine Woche nach der Tag-und-Nacht-Gleiche des Frühjahrs stand der Mond wie im Bild dargestellt mit stark geneigter Sichel etwas tiefer als der Jupiter über dem westlichen Horizont.
Moon - Artemis II - April 6 2026 (55193552739).jpg|Die Aufnahme der Artemis-II-Besatzung während ihres Mondvorbeiflugs am 6. April 2026 zeigt eine Sonnenfinsternis. Am linken Rand der Mondscheibe ist das schwache Leuchten des aschgrauen Mondlichts zu erkennen, das vom Erdschein der links vom Mond befindlichen Erde hervorgerufen wurde.
</gallery>
=== Bestimmung der Kontraste ===
[[Datei:Altlicht.Mond.P1116624.jpg|mini|rechts|hochkant=2|Altlicht des Mondes am 3. November 2021 mit aschgrauem Licht im Schattenbereich des Mondes von Berlin aus gesehen.]]
Das nebenstehende Bild des Altlichts (Mondalter 27,7 von 29,5 Tagen) im Sternbild Jungfrau wurde am 3. November 2021 um 7:05 Uhr morgens mit einem hochwertigen Teleobjektiv mit guter Kontrastübertragung über dem ostsüdöstlichen Horizont (Azimut 122 Bogengrad) in Berlin aufgenommen. Die Sonne stand zum Zeitpunkt der Aufnahme noch gut ein Bogengrad unter dem Horizont, der Mond hatte eine nördliche ekliptikale Breite von gut 3 Bogengrad und eine Elongation von 22 Bogengrad, so dass er bei wolkenlosem Himmel und bei einer Höhe von 17,5 Bogengrad über dem Horizont gut zu beobachten war. Die Mondscheibe war mit bloßem Auge vollständig sichtbar und war zu 3,7 Prozent vom direkten Sonnenlicht beleuchtet.
Die aufgenommenen Rohdaten wurden für diese Auswertung mit einer Software mit möglichst neutralen Einstellungen zu einer Schwarz-Weiß-Aufnahme entwickelt (Kontrast = 1, Gamma = 1, Farbtemperatur = 4100 Kelvin, Farbsättigung = 0). Anhand der Bilddaten können die Luminanzwerte (Helligkeiten) im Bild bestimmt werden, um aus beliebigen paarweisen Luminanzwerten den jeweiligen Kontrast zu berechnen. Der '''Michelson-Kontrast''' <math>M</math> (auch '''Modulation'''), der sich aus zwei benachbarten Luminanzwerten <math>L_2</math> und <math>L_1</math> ergibt (Bedingung: <math>L_2 \ge L_1 \ge 0</math>), liegt immer zwischen 0 (<math>L_2 = L_1</math>, also kein Kontrast) und 1 beziehungsweise 100 Prozent (<math>L_1 = 0</math>, also maximaler Kontrast):
:<math>M = \frac {L_2 - L_1} {L_2 + L_1}</math>
→ Siehe auch [[Digitale_bildgebende_Verfahren:_Grundlagen#Modulation|Wikibook '''Digitale bildgebende Verfahren''', Kapitel '''Grundlagen''', Abschnitt '''Modulation''']].
Die Sichel ist in der ausgewerteten Aufnahme nicht überbelichtet, hat im Scheitel einen mittleren Luminanzwert von:
:<math>L_{Sichel} = 236</math>
Der den Mond umgebende Himmel hat durch das vorhandene Streulicht in der Troposphäre einen mittleren Luminanzwert von:
:<math>L_{Himmel} = 15</math>
Daraus resultiert ein entsprechend hoher Michelson-Kontrast <math>M</math>:
:<math>M_{Sichel,Himmel} = \frac {L_{Sichel} - L_{Himmel}} {L_{Sichel} + L_{Himmel}} = \frac {236 - 15} {236 + 15} = \frac {221} {251} = 0,88</math>
Im aschgrauen Licht in der Schattenseite des abnehmenden Mondes (dessen gesamte scheinbare Helligkeit beträgt bei der in der Abbildung sichtbaren Mondphase übrigens ungefähr -3<sup>m</sup>), die vom Erdschein beleuchtet wird, ergibt sich für die etwas variierende Reflektivität der Mondoberfläche ein mittlerer Luminanzwert von:
:<math>L_{Schatten} = 24,5</math>
Im Vergleich zur hellen Mondsichel ergibt sich daraus ein etwas geringerer Kontrast zwischen Mondsichel und Mondschatten als zwischen Mondsichel und Nachthimmel:
:<math>M_{Sichel,Schatten} = \frac {L_{Sichel} - L_{Schatten}} {L_{Sichel} + L_{Schatten}} = \frac {236 - 24,5} {236 + 24,5} = \frac {211,5} {260,5} = 0,81</math>
Die Standardabweichung beträgt in der Schattenseite des abnehmenden Mondes:
:<math>L_{Abweichung} = 2,5</math>.
Innerhalb der sichtbaren Schattenseite des Mondes resultiert aus den dort mit einer Standardabweichung um den Mittelwert herum auftretenden Luminanzwerten für das aschgraue Licht ein Kontrast von nur 10 Prozent:
:<math>M_{Schatten} = \frac {(L_{Schatten} + L_{Abweichung}) - (L_{Schatten} - L_{Abweichung})} {(L_{Schatten} + L_{Abweichung}) + (L_{Schatten} - L_{Abweichung})} = \frac {L_{Abweichung}} {L_{Schatten}}= \frac {2,5} {24,5} = 0,10</math>
Dieser Kontrastwert ergibt sich im Übrigen im gleichen Maße auch auf der von der Sonne voll beleuchteten und dann jedoch insgesamt erheblich helleren Oberfläche des Vollmonds (die scheinbare Helligkeit beträgt dann ungefähr -13<sup>m</sup>).
:'''Anmerkung''': ''Die Sinnhaftigkeit der Kontrastbestimmung mit der Standardabweichung begründet sich in der Tatsache, dass eine perfekte schwarz-weiße Kante mit einem maximalen Luminanzwert <math>L_{max}</math> im hellen Bereich und einem Luminanzwert von Null im dunklen Bereich eine Modulation <math>M</math> von eins hat, sofern die weiße und die schwarze Teilfläche gleich groß sind und jeweils <math>\frac {n} {2}</math> Messwerte beinhalten. Sowohl der Mittelwert aller <math>n</math> Luminanzwerte als auch die Standardabweichung aller <math>n</math> Luminanzwerte sind dann beide genau halb so groß wie der maximale Luminanzwert:''
::<math>L_{Mittelwert} = \sum_{i=1}^n L_{i} = \sum_{i=1}^{\frac {n} {2}} 0 + \sum_{i=1}^{\frac {n} {2}} L_{max} = \frac {L_{max}} {2}</math>
::<math>L_{Standardabweichung} = \sqrt {\frac {\sum_{i=1}^n (L_{i} - L_{Mittelwert})^2} {n}} = \sqrt {\frac {\sum_{i=1}^n \left( \frac {L_{max}} {2} \right)^2} {n}} = \sqrt {\frac {n \cdot \left( \frac {L_{max}} {2} \right)^2} {n}} = \frac {L_{max}} {2}</math>
:''Der Mittelwert zuzüglich der Standardabweichung entspricht dann dem maximalen Luminanzwert:''
::<math>L_{Mittelwert} + L_{Standardabweichung} = \frac {L_{max}} {2} + \frac {L_{max}} {2} = L_{max}</math>
:''Und der Mittelwert abzüglich der Standardabweichung hat dann den Wert Null:''
::<math>L_{Mittelwert} - L_{Standardabweichung} = \frac {L_{max}} {2} - \frac {L_{max}} {2} = 0</math>
:''Der auf diesem Weg bestimmte Kontrast stimmt mit dem oben genannten Kontrast an der Kante überein:''
::<math>M = \frac {L_{max} - 0} {L_{max} + 0} = \frac {L_{max}} {L_{max}} = 1</math>
Schließlich kann auch noch der Kontrast an der Außenkante der nur vom Erdschein beleuchteten Mondscheibe bestimmt werden, also zwischen aschgrauem Mondlicht und Himmel:
:<math>M_{Schatten,Himmel} = \frac {L_{Schatten} - L_{Himmel}} {L_{Schatten} + L_{Himmel}} = \frac {24,5 - 15} {24,5 + 15} = \frac {9,5} {39,5} = 0,24</math>
Dieser Kontrast ist also deutlich höher als der innerhalb des Mondschattenbereichs. Die Helligkeit wechselt an der Kante der Mondscheibe zudem schlagartig und ist deswegen besser wahrnehmbar als die Helligkeitsunterschiede innerhalb des Schattenbereiches, wie im Folgenden ausgeführt wird.
=== Wahrnehmungsphysiologie ===
[[Datei:CSF.against.angular.frequency.in.cycles.per.degree.png|mini|rechts|hochkant=2|Kontrastempfindlichkeitsfunktion (englisch: Contrast Sensitivity Function, CSF) des menschlichen Auges über der Ortsfrequenz <math>f_a</math> in Linienpaaren pro Grad.]]
Strukturen, bei denen sich die Helligkeit innerhalb eines achtel Bogengrades (7,5 Bogenminuten) ändert, werden von einem gesunden menschlichen Auge nahezu perfekt auf die Netzhaut übertragen und sind deshalb am besten wahrzunehmen. Weichere Strukturen mit sich langsamer und kontinuierlich ändernder Helligkeit oder kleinere Strukturen sind bei gleichem Objektkontrast schwieriger zu erkennen. Ob ein bestimmter Objektkontrast mit bloßem Auge wahrgenommen werden kann, kann mit Hilfe der Kontrastempfindlichkeitsfunktion (Contrast Sensitivity Function (CSF)) des menschlichen Auges ermittelt werden, bei der die Kontrastübertragung von einem Objekt auf die Netzhaut des Auges über der Ortsfrequenz in Linienpaaren pro Bogengrad angegeben ist:
→ Siehe auch [[Digitale_bildgebende_Verfahren:_Grundlagen#Kontrastempfindlichkeitsfunktion|Wikibook '''Digitale bildgebende Verfahren''', Kapitel '''Grundlagen''', Abschnitt '''Kontrastempfindlichkeitsfunktion''']].
[[Datei:CSF.UHD.png|mini|rechts|hochkant=2|Zur Kontrastempfindlichkeitsfunktion des menschlichen Auges mit den Ortsfrequenzen von 2 sinusmodulierten Linienpaaren pro Bildbreite (linke Bildhälfte) bis hin zu 1024 Linienpaaren pro Bildbreite (rechts am Bildrand), Bildgröße 3860 mal 2160 Bildpunkte (16:9) mit maximalem Kontrast unten, ohne Kontrast oben.]]
Das nebenstehende Diagramm und die darunter befindliche unfarbige Graphik mit den sinusförmigen Helligkeitsmodulationen visualisieren dies. Von links nach rechts wächst die Ortsfrequenz ausgehend von zwei Linienpaaren pro Bildbreite immer weiter, und der Kontrast ändert sich in vertikaler Richtung kontinuierlich zwischen den Extremwerten 0 und 1. Bei großen und sehr kleinen Ortsfrequenzen sind geringe Kontraste nicht so gut zu erkennen wie im mittleren Bereich. Die maximale Empfindlichkeit wird bei acht Linienpaaren pro Bogengrad erreicht und hängt bei einem gegebenen Objekt demzufolge vom Betrachtungsabstand ab.
Das Auge kann nach dieser Kontrastempfindlichkeitsfunktion innerhalb eines Bogengrades maximal bis zu 80 Linien mit maximalem Objektkontrast (also 40 Linienpaare mit dem Kontrast 1) wahrnehmen. In diesem Fall liegt der wahrgenommene Kontrast zwischen den benachbarten Linien bei ungefähr 5 Prozent. Bei 120 Linien mit maximalem Objektkontrast (also 60 Linienpaare mit dem Kontrast 1) sinkt der wahrgenommene Kontrast auf unter ein Prozent und ist somit praktisch nicht mehr wahrnehmbar. Wegen des scheinbaren Winkeldurchmessers der Mondscheibe von 0,5 Bogengrad, kann das Auge innerhalb des Monddurchmessers also maximal 20 Linienpaare beziehungsweise 40 Linien unterscheiden. Insbesondere an der scharfen Kante der Mondscheibe ist eine ins Auge nahezu verlustfrei übertragene Ortfrequenz von acht Linienpaaren pro Bogengrad stark vertreten, so dass der in der obigen Aufnahme des Mondes vorhandene Objektkontrast von 24 Prozent zwischen Mondschatten und Himmel (siehe oben) gut wahrgenommen werden kann.
Etwas schwieriger gestaltet sich dies innerhalb des Schattenbereichs des Mondes. In der obigen Aufnahme tauchen die stärksten Kontraste entlang einer Linie von oben nach unten auf, wo die helleren Mondebenen mit den Kratern Copernicus und Tycho durch die dunkleren Maria Imbrium und Nubium unterbrochen werden. Daraus resultiert eine Ortsfrequenz mit fünf Linienpaaren pro Bogengrad, wo die Kontrastempfindlichkeitsfunktion ebenfalls einen recht hohen Wert von 0,9 beziehungsweise 90 Prozent erreicht. Dies schwächt den auf der Mondoberfläche vorhandenen Objektkontrast von 10 Prozent im vom Auge auf die Netzhaut übertragenen Bildkontrast nur geringfügig ab.
Aus diesen Überlegungen resultiert, dass die im aschgrauen Licht des Mondes wahrnehmbaren Kontraste knapp 10 Prozent betragen. Diese sind zwar geringer als der Kontrast von 24 Prozent zwischen dem aschgrauen Mondlicht und dem den Mond umgebenden Himmel, sie liegen allerdings noch in einem wahrnehmbaren Bereich. Somit ist erwiesen, dass ein gesundes Auge unter idealen Beobachtungsbedingungen auch völlig ohne optische Hilfsmittel die Helligkeitsunterschiede und Strukturen im nur durch den Erdschein erhellten Schattenbereich des Mondes erkennen kann.
Es gibt jedoch zahlreiche Faktoren, die den wahrnehmbaren Kontrast deutlich herabsetzen oder die Möglichkeit der Wahrnehmung verhindern können. Je älter ein Mensch wird, desto mehr Streulicht wird durch kleinste Verletzungen beziehungsweise Narben in der Hornhaut oder in der Linse des Auges erzeugt. Dieses Streulicht erzeugt einen mehr oder weniger gleichmäßigen Lichtschleier, der auch die dunklen Stellen auf der Netzhaut beleuchtet. Dies führt in jedem Fall zu einer Herabsetzung des wahrnehmbaren Kontrastes und verhindert somit das Erkennen von schwachen Kontrasten.
=== Begrenzende Einflüsse ===
Aber auch in der Natur gibt es viele begrenzende Faktoren. Hierzu gehört das natürliche Streulicht durch die Sonne in der Troposphäre, wenn diese zu dicht am Mond oder zu dicht unter dem Horizont oder gar über dem Horizont steht. In bewohnten Gegenden kommt als großer Störfaktor noch die Lichtverschmutzung hinzu. Ferner wird der Objektkontrast durch atmosphärische Störungen vermindert. Diese ergeben sich zum einen durch Dunst, Nebel oder Staub in der Atmosphäre, und zum anderen aus den langen Wegstrecken, die das Licht bei einer Beobachtung eines Himmelsobjekts durch die Troposphäre zurücklegen muss, wenn dieses sehr dicht über dem Horizont steht. Hierbei wird das den Beobachter erreichende Licht nicht nur abgeschwächt ('''Extinktion'''), sondern auch durch das Licht aller möglichen anderen Lichtquellen angereichert, das an den Luftmolekülen gestreut oder durch Mikroturbulenzen abgelenkt wurde, so dass sich die minimal vorhandene Helligkeit vergrößert und der Kontrast somit verringert. In bewohnten Regionen ist zudem mit erheblicher Lichtverschmutzung durch künstliche Lichtquellen zu rechnen.
Auf der anderen Seite wird insbesondere in Horizontnähe der blaue Lichtanteil von astronomischen Objekten auf dem Lichtweg durch die Troposphäre durch die stark wellenlängenabhängige '''Rayleigh-Streuung''' seitlich abgelenkt, so dass es praktisch gar nicht mehr zum Beobachter gelangt. Dies führt zu den rötlichen Auf- und Untergängen von Sonne und Mond sowie zur Blaufärbung des Taghimmels.
Unter Umständen muss das Licht mehrere hundert Kilometer durch die Atmosphäre zurücklegen, wie in der folgenden Abbildung zu erkennen ist:
[[Datei:Troposphaerische.Laengen.png|mini|zentriert|hochkant=4|Weglängen für Lichtstrahlen in der 15 Kilometer hohen Troposphäre in Abhängigkeit von der Zenitdistanz.]]
=== Beobachtung ===
Die Beobachtungsmöglichkeiten des aschgrauen Lichts werden grundsätzlich durch die folgenden, sich teilweise widerstrebenden Bedingungen verbessert:
* Die '''Lichtverschmutzung''' in der Atmosphäre ist gering.
* Das '''Mondalter''' beträgt wenige Tage vor oder nach Neumond.
* Die '''Sonne''' steht möglichst weit unter dem Horizont.
* Der '''Mond''' hat eine möglichst große Höhe über dem Horizont.
* Die '''Mondsichel''' ist möglichst klein, um möglichst wenig Streulicht zu verursachen.
Für die Beobachtung aus nördlichen geographischen Breiten gilt insbesondere:
* Der Mond hat eine möglichst große '''nördliche ekliptikale Breite''' zwischen seinem aufsteigenden und seinem absteigenden Knoten.
* Die '''Ekliptik''' steht möglichst steil auf dem Horizont. Dies ist bei den Tag-und-Nacht-Gleichen der Fall, besonders gut
** abends im Frühjahr beim '''Neulicht''' des Mondes nach Sonnenuntergang im Westen.
** morgens im Herbst beim '''Altlicht''' des Mondes vor Sonnenaufgang im Osten.
=== Farbigkeit ===
[[Datei:Neulicht.21.3.2026.HDR.1024.png|mini|hochkant=2|Die Abbildung zeigt eine Hochkontrastaufnahme des Erdscheins im Mondschatten zum Frühlingbeginn drei Tage nach Neumond mit Sternen bis zur zehnten Größenklasse (10<sup>m</sup>) in der Umgebung. Die Sichel zeigte einen Tag nach der Tag-und-Nacht-Gleiche im Frühjahr 2026 und zwei Tage vor der großen Mondwende bei einer nördlichen ekliptikalen Breite des Mondes von vier Bogengrad eine besonders starke Neigung. Die Sonne war zuvor im Westen untergegangen und stand bereits elf Bogengrad unter dem Horizont. Die Symmetrieachse der Mondsichel (-7<sup>m</sup>) auf der zu 9,3 Prozent beleuchteten Mondscheibe zeigt nach rechts unten in Richtung der Sonne (Farbtemperatur = 4100 Kelvin, Belichtungsreihe mit sieben Bildern in ganzen Blendenstufen).]]
Bei genauer Betrachtung und Beobachtung ist das sogenannte aschgraue Licht gar nicht farbneutral, sondern spiegelt einen mittleren Farbton der von der Sonne beleuchteten Erdoberfläche wider, von der wiederum der Mondschatten beleuchtet wird. Je niedriger der Bewölkungsgrad, desto stärker ist zudem der Einfluss der farbigen Oberfläche der Erde. Die Farben der Erdoberfläche schwanken hierbei deutlich zwischen den Ozeanen, Wäldern oder Wüsten. In der nebenstehenden Hochkontrastaufnahme zeigt der Mondschatten im HSV-Farbraum einen mittleren Farbwert mit dem Farbwinkel 202°, was als Farbton blaucyan übersetzt werden kann. Dies entspricht recht gut dem mittleren Farbton der der Sonne zugewandten Seite der Erde ("Blue Marble") während der Aufnahme, der für das hier gezeigte Beispielbild im HSV-Farbraum einen mittleren Farbwert mit dem Farbwinkel 201° hat. Der Farbwinkel ist von der Sättigung oder der Helligkeit nicht abhängig.
[[Datei:Neulicht.21.3.2026.HDR.Original.Durchschnitt.gesaettigt.aufgehellt.png|mini|links|hochkant=4|Sichtbarmachung des Farbwerts mit dem Farbwinkel 202° des aschgrauen Mondlichts in einer Hochkontrastaufnahme des Erdscheins im Mondschatten zum Frühlingbeginn drei Tage nach Neumond.<br/>Von links nach rechts:<br/>1. Originalaufnahme<br/>2. Mittelwert<br/>3. Farbe gesättigt<br/>4. aufgehellt]]
[[Datei:North America from low orbiting satellite Suomi NPP.Original.Durchschnitt.gesaettigt.abgedunkelt.png|mini|links|hochkant=4|Sichtbarmachung des Farbwerts mit dem Farbwinkel 201° der Erdoberfläche im Bereich von Nordamerika und dem südöstlichen Pazifik.<br/>Von links nach rechts:<br/>1. Originalaufnahme<br/>2. Mittelwert<br/>3. Farbe gesättigt<br/>4. abgedunkelt]]
<div style="clear:both"></div>
==Einzelnachweise==
<references></references>
<noinclude>
[[Kategorie:Astronomische Kuriositäten]]
</noinclude>
9n6md8ass9me5gxv9skob8y6r3f73cs
Gödel
0
117878
1092180
1092163
2026-10-17T07:47:47Z
Santiago
19191
/* Kurt GÖDEL und der sog. ‚ontologische Gottesbeweis‘ */ Zusatz
1092180
wikitext
text/x-wiki
[[Kategorie: Buch]]
{{Regal|ort=Philosophie}}
{{Vorlage:StatusBuch|9}}
==<div class="center"><span style="color:#660066">'''Kurt GÖDEL und der sog. ‚ontologische Gottesbeweis‘'''</span></div>==
<span style="font-family: Times;"><big><div class="center">לַמְנַצֵּ֗חַ לְדָ֫וִ֥ד אָ֘מַ֤ר נָבָ֣ל בְּ֭לִבּוֹ אֵ֣ין אֱלֹהִ֑ים הִֽשְׁחִ֗יתוּ הִֽתְעִ֥יבוּ עֲלִילָ֗ה אֵ֣ין עֹֽשֵׂה־טֽוֹב׃
(Psalm 14,1)</div></big></span>
----
===<div class="center"><span style="color:#660066">Vorwort</span></div>===
Zur Orientierung ''':''' Die Diskussion um GOTT, <span style="color:#4C58FF">— ‚'''G'''‘ —</span>, läuft schon über zweitausend Jahren. Vor etwa tausend Jahren hat sich ein gewisser ANSELM gesagt ''':''' <span style="color:#FF6000">»''Ich glaube an GOTT'' … <span style="color:#00B000">[ sonst wäre er sicher
nicht Erzbischof von Canterbury geworden ]</span> … ''aber ich möchte auch wissen und verstehen, ob das stimmt und sinnvoll ist, was ich da glaube '''!''''' «</span> Dann hat er seine Überlegungen dazu aufgeschrieben, und das kann man in seinen Schriften auch heute noch lesen. Der sehr geschätzte deutsche Professor und Philosoph aus Königsberg, Immanuel KANT, hat das, was ANSELM da aufgeschrieben hat, gelesen, <span style="color:#00B000">(vermittelt durch CARTESIUS)</span>, und das dann den <span style="color:#FF6000">„ontologischen Gottesbeweis“</span> genannt, <span style="color:#00B000">(obwohl es in Wirklichkeit gar kein Gottesbeweis ist; genauer ''':''' es ist kein Beweis für die Existenz GOTTES)</span>, und dieser große KANT hat dann großartig bewiesen, das, was ANSELM da aufgeschrieben hat, sei falsch. Es sei <span style="color:#FF6000">„ja gar kein“</span> Gottesbeweis '''!''' <span style="color:#00B000">( Naja, was denn sonst '''?''' )</span> Wobei er den Fehler gemacht hat, dass er den, an sich, unvergleichbaren GOTT mit hundert Talern in seinem Vermögenszustande verglichen hat. <span style="color:#00B000">(Das ist aber eine andere Geschichte.)</span> Hundert Taler und GOTT haben ,an sich‘ nichts gemeinsam, außer, wenn KANT ,wirklich‘ hundert Taler hat, und GOTT auch ,wirklich‘ existiert, <span style="color:#00B000">(wie ANSELM und gläubige Menschen glauben)</span>, dann gibt es GOTT und die Taler eben ,wirklich‘. Aber damit ist man nicht schlauer geworden. Seit KANT läuft die ganze Diskussion um GOTT immer nur als Diskussion um den <span style="color:#00B000">(von KANT)</span> so genannten <span style="color:#FF6000">„ontologischen, <span style="color:#00B000">[ kosmologischen, teleologischen etc. ]</span> Gottesbeweis“</span> —, obwohl es niemals einen Beweis für die Existenz GOTTES geben kann und niemals geben wird. <span style="color:#00B000">(Das haben Wissenschaftler jeder Richtung und Philosophen aller Weltanschauungen uns immer wieder nachdrücklich versucht zu sagen, weil keiner dieser sog. Beweise für die Existenz eines GOTTES stringent ist. ,Existenz‘, an sich, ist, — nach ARISTOTELES — ,unbeweisbar‘. Sie ist, als frei wählbare Prämisse, immer nur der fixe und nicht hinterfragbare Ausgangspunkt einer möglichen logischen Beweisführung. Auf die Existenz kann man nur ,hinweisen‘.)</span> Für ANSELM selbst, wäre es ,Unsinn‘, einen Beweis für die Existenz GOTTES verlangen zu wollen. Beweisen, bzw. darauf ,hinweisen‘, kann man, z. B. auf die Existenz von Naturgesetzen. Die sind unveränderlich und fix, immer und überall. Jeder vernünftige Mensch muss sie akzeptieren. Man kann darüber nicht diskutieren und sie dann mit Mehrheitenbeschlüsse verändern. Wenn GOTT ebenso ,bewiesen‘ werden könnte, dann wäre jeder vernünftige Mensch gezwungen, die Existenz GOTTES wie ein Naturgesetz anzunehmen. Gott ist aber kein Naturgesetz. GOTT ist <span style="color:#FF6000">„Person“</span>, — für Christgläubige ''':''' <span style="color:#FF6000">„ein GOTT in drei Personen“</span>. Und GOTT ist <span style="color:#FF6000">„Geist“</span>. Das besagt, dass GOTT nicht mit materiellen Dingen aus unserer Welt verglichen werden darf; <span style="color:#00B000">(was sowohl THOMAS von Aquin als auch Immanuel KANT doch getan haben)</span>. Und ganz wesentlich ''':''' der Zugang zu GOTT läuft nicht über den Beweis, sondern immer nur über den Glauben. Wer an GOTT glauben will, dem antwortet GOTT auf seine Weise — nämlich <span style="color:#FF6000">„geistig“</span> bzw. <span style="color:#FF6000">„geistlich“</span>. Wer nicht an GOTT glauben will, dessen Entscheidung respektiert GOTT, und drängt sich nicht auf. Glaube ist immer eine freie Entscheidung des Menschen für GOTT. Niemand darf gezwungen werden. Wenn es einen ,Beweis‘ für GOTT gäbe, wäre jeder vernünftige Mensch gezwungen, an GOTT zu glauben. Und das widerspricht ganz entschieden der Freiheit des Glaubens. Daher gibt es nie und nimmermehr einen Existenzbeweis für GOTT '''!!!''' ... Daher darf man das Kalkül des Logiker GÖDEL, <span style="color:#00B000">(und das Theorem ANSELMS)</span>, auch nicht als einen Existenzbeweis für GOTT lesen. Sowohl ANSELM als auch GÖDEL setzen die Existenz GOTTES notwendig als gegeben schon voraus. Das GÖDEL-System, und auch das Theorem ANSELMS, sind bloß die logische Bestätigung der Widerspruchsfreiheit und Sinnhaftigkeit der Glaubensüberzeugung eines Menschen, der sich schon entschieden hat, an GOTT zu glauben; und nicht der Grund für seine Entscheidung. Alle sogenannten ,Gottesbeweise‘, sind in Wirklichkeit nichts anderes, als nachträgliche Evaluierungen eines GOTT-Glaubens, bzw. ,Wege‘, <span style="color:#00B000">(bei THOMAS von Aquin)</span>, die aufzeigen, dass der GOTT-Glaube widerspruchsfrei, sinnvoll, und <span style="color:#FF6000">»''mit allen bekannten Tatsachen durchaus vereinbar ist''«</span>, wie GÖDEL sagt. GOTT hat es nicht nötig, ,bewiesen‘ <span style="color:#00B000">( ╞ )</span> zu werden.
===<div class="center"><span style="color:#660066">Einleitung</span></div>===
Eine Studie zum GÖDEL-Kalkül. Der Logiker Kurt GÖDEL <span style="color:#00B000">(1906-1978)</span> hat mit diesem Kalkül eine moderne Rekonstruktion des, <span style="color:#00B000">(von KANT)</span>, so genannten ‚ontologischen Gottesbeweises‘ nach ANSELM von Canterbury auf modal-logischer Basis vorgelegt. Damit hat er die <span style="color:#FF6000">„Rede von GOTT“</span> auf eine ‚vernünftige Basis‘ gestellt, d.h. sie soll für jeden Menschen nachvollziehbar sein, <span style="color:#FF6000">»''rein verstandesmäßig, (ohne sich auf den Glauben an irgendeine Religion zu stützen)''«</span>, wie er sagt. GÖDEL ,nimmt‘ als Logiker, angeregt durch den Philosophen und Mathematiker Gottfried Wilhelm LEIBNIZ, rein theoretisch, fürs Erste, einmal ‚an‘, <span style="color:#00B000">(als Prämisse, d.i. der Term :01: im 3. Beweisgang zum Theorem ANSELMS im Anhang)</span>, dass es GOTT gibt ''':''' d.i. ein sog. ,methodologischer GOTT-Glaube‘, und untersucht die logischen Konsequenzen. Dabei zeigt sich, beim genaueren Hinsehen, dass der ‚Nicht-GOTT-Glaube‘, der ‚dezidierte‘ Atheismus, <span style="color:#00B000">(im Beweisgang ,Widerlegung‘ im Anhang)</span>, überraschender Weise, zu einem Widerspruch führt, und damit logisch ,falsch‘ ist. Jedoch, GÖDEL kann und will mit seinem Kalkül keinen ,GOTT-Glauben‘ ,erzeugen‘, d.h. das GÖDEL-Kalkül ist kein <span style="color:#FF6000">„Existenz-Beweis"</span> für den GOTT der Bibel, sondern, es setzt die Existenz eines GOTTES, einfach als gegeben, schon voraus. GÖDEL beweist dann mit seinem System, dass der traditionelle abendländische ,GOTT-Glaube‘, <span style="color:#FF6000">»''die theologische Weltanschauung''«</span>, <span style="color:#00B000">(d.i. die Kalkül-Prämisse, und das, daraus ,regulär‘ (├ ) abgeleitete, Theorem ANSELMS)</span>, mit den Maßstäben der modernen Logik <span style="color:#FF6000">»''durchaus vereinbar''«</span> <span style="color:#00B000">(d.h. logisch ,richtig‘)</span> ist, im Gegensatz zum ,Nicht-GOTT-Glauben‘, der davon ausgeht, dass es keinen GOTT gibt. GÖDEL beweist mit seinem System ''':''' der traditionelle GOTT-Glaube ist, — mit <span style="color:#FF6000">»''mathematisch-logischer Evidenz''«</span> —, widerspruchfrei und daher wahr. <span style="color:#00B000">(Der Beweis aus dem Widerspruch des Gegenteils ist ein ,indirekter Beweis‘ und kein ,Zirkelbeweis‘ '''!''' )</span> GÖDEL blieb bis zu seinem Tod ohne ein dezidiertes religiöses Bekenntnis. <span style="color:#00B000">(Das Leben ist nicht immer ,logisch‘.)</span>
Entsprechend der <span style="color:#FF6000">»''theologischen Weltanschauung''«</span> ist GOTT der Größte, <span style="color:#FF6000">»''über dem nichts Größeres mehr gedacht werden kann''«</span>, <span style="color:#00B000">(ANSELM)</span>; bzw. <span style="color:#FF6000">»''GOTT ist ein Wesen von äußerster Größe und Vollkommenheit, das alle Grade derselben in sich schließt''«</span>, <span style="color:#00B000">(LEIBNIZ)</span>, und der für uns immer schon ,da‘ ist. Das ist die methodologische Prämisse des GÖDEL-Kalküls. Davon ausgehend, zeigen Axiom-1 und Axiom-2, dass es zu einem Widerspruch führt, falls man ,annimmt‘, es sei nicht möglich, dass es <span style="color:#FF6000">„GOTT“</span>, <span style="color:#4C58FF">— ‚'''G'''‘ —</span> gibt. Aus dem Widerspruch des Gegenteils wird von GÖDEL, mit dem <span style="color:#4C58FF">[ Modus tollendo tollens ] :: [ A → B, ¬B ├ ¬A ]</span>, dann ,bewiesen‘ <span style="color:#00B000">( ╞ )</span> ''':''' es ist doch möglich, dass es <span style="color:#FF6000">„GOTT“</span> wirklich gibt. Somit ist der Glaube an <span style="color:#FF6000">„GOTT“</span>, — weil widerspruchsfrei —, mit den Worten GÖDELS ''':''' <span style="color:#FF6000">»''mit allen bekannten Tatsachen durchaus vereinbar''«</span>.
Immanuel KANT <span style="color:#00B000">(1724-1804)</span> scheint diesen Fall vorausgesehen zu haben, dass versucht werden könnte, die ,Möglichkeit‘ GOTTES aus einem Widerspruch zu ,beweisen‘ ''':'''
::<span style="color:#FF6000">»</span> <span style="color:#00B000">[ Angenommen, es gibt ]</span> ''doch einen und zwar nur diesen '''Einen''' Begriff'' <span style="color:#00B000">[ ,GOTT‘, so dass ]</span> ''das Nichtsein oder das Aufheben seines Gegenstandes'' <span style="color:#00B000">[ GOTT ]</span>, ''in ,sich selbst‘ widersprechend sei; und dieses ist der Begriff des allerrealsten Wesens. Es hat, sagt ihr, alle Realität'', <span style="color:#00B000">[ bzw. alle Vollkommenheit ]</span>, ''und ihr seid berechtigt, ein solches Wesen als ,möglich‘ anzunehmen'' ... <span style="color:#00B000">[ denn das GÖDEL-Kalkül ,beweist‘ ( ╞ ) in der ,Widerlegung‘ im Anhang, wie auch im 1. Beweisgang, aus einem Widerspruch, dass die Existenz GOTTES definitiv logisch ,möglich‘ ist. ]</span> … ''obgleich der sich nicht widersprechende'', <span style="color:#00B000">[ ,mögliche‘ ]</span>, ''Begriff'', <span style="color:#00B000">[ ,GOTT‘ ]</span>, ''noch lange nicht die'' <span style="color:#00B000">[ reale ]</span> ''Möglichkeit des Gegenstandes'', <span style="color:#00B000">[ GOTT ]</span>, ''beweiset. … Das ist eine Warnung, von der Möglichkeit der Begriffe (logische)'', <span style="color:#4C58FF">— '''◇''' —</span>, ''nicht sofort auf die Möglichkeit der Dinge (reale)'', <span style="color:#4C58FF">— '''□''' —</span>, ''zu schließen''<ref>vgl. ‚''<span style="font-family: Times;"><big>Kritik der reinen Vernunft</big></span>''‘, Seite 399; https://www.korpora.org/kant/aa03/399.html</ref>. <span style="color:#00B000">[ Trotz dieser Warnung, wird dieser Schluss dennoch im Theorem ANSELMS vollzogen, bzw. mit GÖDEL im 3. Beweisgang ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx→□∃xGx'''‘ —</span> '''!''' ]</span>. <span style="color:#FF6000">«</span>
Warum das <span style="color:#FF6000">»''mit allen bekannten Tatsachen durchaus vereinbar''«</span> ist, und inwieweit KANT sich irrt, wird in dieser Studie gezeigt.
===<div class="center"><span style="color:#660066">Der Schlüsselbegriff im Kalkül</span></div>===
Der ‚Schlüsselbegriff‘ in diesem Kalkül ist <span style="color:#FF6000">„positive Eigenschaft“</span>, bzw. <span style="color:#FF6000">„Perfektion“, „Vollkommenheit“</span> ''':''' <span style="color:#4C58FF">— ‚'''P'''‘ —</span>. Für diesen wichtigen Begriff gibt es aber im Kalkül selbst keine explizite Definition, sondern er wird nur durch seine Verwendung innerhalb des Kalküls indirekt ‚definiert‘. <span style="color:#00B000">(Das heißt ''':''' <span style="color:#4C58FF"> — ‚'''P'''‘ — </span> bezeichnet ein System von ,Eigenschaften‘, die ,positiv‘, bzw. ,vollkommen‘ | ,perfekt‘ | genannt werden, von denen im Kalkül wohl beweisbar ist, dass sie sich gegenseitig ,nicht widersprechen‘, weil sie im System als solche ,gleichwertig‘, bzw. gleich ,wahr‘ sind, jedoch ohne sie erschöpfend aufzählen zu können, oder auch nur sagen zu können, was sie alle im einzelnen bedeuten, außer, dass sie kompatibel sind.)</span> Mit der Wahl dieses Schlüsselbegriffes hat GÖDEL eine wesentliche Vorentscheidung für die Ergebnisse des Kalküls getroffen '''!''' In seinen Notizen zum ‚ontologischen Beweis‘ vom 10. Februar 1970 gibt GÖDEL, — für die nachträgliche Interpretation dieses Begriffes <span style="color:#00B000">(und auch für das Kalkül selbst)</span> —, die richtungsweisende Erklärung ''':'''
::<span style="color:#FF6000">»''Positiv bedeutet positiv im moralisch ästhetischen Sinne...''«</span>
::Und er fügt in Klammer hinzu ''':'''
::<span style="color:#FF6000">»...''(unabhängig von der zufälligen Struktur der Welt). Nur dann sind die Axiome wahr''.«</span><ref>GÖDEL, Kurt, ''<span style="font-family: Times;"><big>‚Ontological proof‘</big></span>'' in ''<span style="font-family: Times;"><big>‚Collected Works‘</big></span>'', vol. III, ed. S.FEFERMAN et al., Oxford (U.P.), 1995; 403–404.</ref>
GÖDEL-Axiom-1 ''':''' <span style="color:#4C58FF">— ‚('''PX <small><math>{ \color{Blue} \dot\lor}</math></small> P¬X''')‘ ↔<span style="color:#00B000"> ‚('''¬PX ↔ P¬X''')‘</span> — </span> ''':''' <span style="color:#FF6000"> »''Entweder die Eigenschaft'' <span style="color:#4C58FF">‚'''X'''‘</span> ''oder ihre Negation'' <span style="color:#4C58FF">‚'''¬X'''‘</span> ''ist positiv''«</span>. Hier ist der Hauptkritikpunkt, dass es Eigenschaften gibt, die ,an sich‘ weder positiv noch negativ sind. Beispiele wären ''':''' ‚rothaarig‘ oder ‚schwerwiegend‘; solche Eigenschaften können aber ,für mich‘ entweder positiv oder negativ sein, abhängig von meiner Betrachtungsweise und subjektiven Vorlieben. Diese Eigenschaften, wie ‚rothaarig‘ an sich, oder meine positiv-negativen ‚Betrachtungsweisen‘, sind jedoch der <span style="color:#FF6000">»''zufälligen Struktur der Welt''«</span> entnommen und treffen nicht den <span style="color:#FF6000">»''moralisch ästhetischen Sinn''«</span> von <span style="color:#FF6000">»''positiv''«</span> bei GÖDEL. Er orientierte sich an LEIBNIZ, welcher im Bezug zum ‚ontologischen Beweis‘ definiert ''':'''
::<span style="color:#FF6000">»''Vollkommenheit'' <span style="color:#00B000">[ GOTTES ]</span> ''nenne ich jede einfache Eigenschaft, die sowohl positiv als auch absolut ist, oder dasjenige, was sie ausdrückt, ohne jede Begrenzung ausdrückt''.«</span><ref>Zitiert nach Thomas GAWLICK, in ''<span style="font-family: Times;"><big>‚Was sind und was sollen mathematische Gottesbeweise ?‘</big></span>'', Predigt vom 8.1.2012 in der Kreuzkirche zu Hannover. https://web.archive.org/web/20130524164359/http://www.idmp.uni-hannover.de/fileadmin/institut/IDMP-Studium-Mathematik/downloads/Gawlick/Predigt_Gawlick_Gottesbeweise.pdf</ref>
Die Seins-Eigentümlichkeiten <span style="color:#00B000">(Daseinsmodi, Perfektionen)</span> wie ‚wahr‘, ‚gut‘, ‚edel‘ usw. entsprechen dem <span style="color:#FF6000">»''moralisch ästhetischen Sinn''«</span> von <span style="color:#FF6000">»''positiv''«</span> bei GÖDEL. Das sind Beispiele für ‚absolut‘ positive Begriffe aus der Lehre der Seinsanalogie ''':''' <span style="font-family: Times;"><big>‚verissimum‘, ‚optimum‘, ‚nobilissimum‘</big></span>, usw., die, an sich, <span style="color:#FF6000">»''ohne jede Begrenzung gelten''«</span>; zu finden in der <span style="font-family: Times;"><big>‚Via quarta‘</big></span>, bei THOMAS von Aquin, über die <span style="color:#FF6000">„analoge"</span> Abstufung im ‚Sein‘ der Dinge. Diese <span style="color:#FF6000">„analoge"</span> ‚Abstufung‘ ist dann die faktische Begrenztheit <span style="color:#00B000">(d.h. Unvollkommenheit)</span> im <span style="color:#FF6000">»''zufälligen''«</span> ‚Sein‘ der Dinge —. Die <span style="color:#FF6000">„positiven Eigenschaften“</span> GÖDELS ''':''' <span style="color:#4C58FF">— ‚'''P'''‘ —</span>, wie Wahrheit, Einheit, Gutheit, <span style="color:#00B000">(von ,Güte‘)</span>, Schönheit, Adel, <span style="color:#00B000">(von ,edel‘)</span>, Gleichheit, Andersheit, Wirklichkeit, ,Sein‘ im Sinne von Etwas-sein, etc. werden <span style="color:#FF6000">„Perfektionen“</span>, oder auch <span style="color:#FF6000">„Transzendentalia“</span> genannt, <span style="color:#00B000">(von lateinisch ''':''' <span style="font-family: Times;"><big>transcendere</big></span>, <span style="color:#FF6000">„übersteigen“</span>)</span>. In der mittelalterlichen Scholastik sind Transzendentalien die Grundbegriffe, die allem Seienden als <span style="color:#FF6000">„Modus“</span>, <span style="color:#00B000">(d.h. ,Eigentümlichkeit‘, als allgemeine Seinsweisen)</span>, zukommen. Wegen ihrer Allgemeinheit ,übersteigen‘ sie die besonderen Seinsweisen, welche ARISTOTELES die ,Kategorien‘ nannte. Ontologisch betrachtet, werden die Transzendentalien als das allem Seienden Gemeinsame aufgefasst, da sie von allem ausgesagt werden können. Von der KI werden sie, nicht unpassend, als <span style="color:#FF6000">„ultimative Eigenschaften des Seins“</span> bezeichnet, die <span style="color:#FF6000">„jenseits der materiellen Welt existieren“</span>, <span style="color:#00B000">(da sie ,ultimativ‘ nur von GOTT, als den unbegrenzt Vollkommenen, ausgesagt werden können, die jedoch, auch von allen übrigen Seienden, wegen deren seinsmäßigen Unvollkommenheit und Begrenztheit, abgestuft, d.h. <span style="color:#FF6000">„analog"</span>, ausgesagt werden)</span>. Diese Transzendentalien sind ,inakzident‘, das heißt, sie entstehen nicht aus anderen Begriffen, sondern sind erste, unteilbare ,Bestimmungen‘ <span style="color:#4C58FF">— ‚'''X'''‘ —</span> des Denkens und des Seins, die ,allen Seienden‘ <span style="color:#4C58FF">— ‚'''∀x'''‘ —</span>, unabhängig von ihren speziellen Eigenschaften, als ,relative‘ Vollkommenheiten, <span style="color:#00B000">(bzw. Unvollkommenheiten)</span>, mit ,modaler‘ Notwendigkeit, <span style="color:#4C58FF">— ‚□‘ —</span>, <span style="color:#FF6000">„analogisch"</span> zukommen ''':''' <span style="color:#4C58FF">— ‚'''□∀xXx'''‘ —</span>, d.h. sie sind in allen Seienden, seinsmäßig abgestuft und abgegrenzt, ,relativ‘ zum unbegrenzt Unendlichen ihrer selbst, und <span style="color:#FF6000">»''dasjenige, was sie ausdrücken, drücken sie ohne jede Begrenzung aus''«</span>, <span style="color:#00B000">(LEIBNIZ)</span>; und damit stehen sie ontologisch in einer <span style="color:#FF6000">„analogen"</span> ,Beziehung‘ zu GOTT, dem absolut Vollkommenen und unbegrenzt Unendlichen. In der Erkenntnisordnung wirken sie als die ersten Begriffe des menschlichen Verstehens, die eine Basis für alle weiteren wissenschaftlichen Erkenntnisse bilden. In der Seinsordnung sind die Transzendentalien ontologisch eins, gleichwertig, <span style="color:#00B000">(,mathematisch äquivalent‘)</span>, und daher konvertierbar, d.h. austauschbar, <span style="color:#00B000">(vgl. z. B. den scholastischen ,Merksatz‘ ''':''' <span style="font-family: Times;"><big>,ens et verum convertuntur‘</big></span>)</span>. Damit sind sie auch von einander abhängig, was GÖDEL sowohl im Axiom-2 ''':''' <span style="color:#4C58FF">— ‚'''PX ∧ □∀x(Xx→Yx)→PY'''‘ —</span>, für positive Eigenschaften, als auch in der Definition-2 ''':''' <span style="color:#4C58FF">— ‚'''X'''<sub>ess</sub>'''x ↔ Xx ∧ ∀Y(Yx→ □∀y(Xy→Yy))'''‘ —</span>, für die Wesenseigenschaften, syntaktisch formalisiert hat mit dem Term-Element ''':''' <span style="color:#4C58FF">— ‚'''□∀x(Xx→Yx)'''‘ —</span>, weil sie sich wechselseitig, — mit ,modaler‘ Notwendigkeit —, gleichwertig ,implizieren‘, d.h. einschließen, <span style="color:#00B000">(im Axiom-2 mit der Individuum-Variable <span style="color:#4C58FF"> — ‚'''x'''‘ —</span>, und in der Definition-2 mit der Individuum-Variable <span style="color:#4C58FF"> — ‚'''y'''‘ — </span>, die, als noch unbestimmte Variablen, frei austauschbar sind)</span>. Man kann die Transzendentalia, <span style="color:#00B000">(wie GÖDEL)</span>, auch ,Wesenseigenschaften‘ ''':''' <span style="color:#4C58FF"> — ‚'''X'''<sub>ess</sub>‘ —</span>, nennen, weil sie allem Seienden <span style="color:#4C58FF">— ‚'''□∀y'''‘ —</span> <span style="color:#FF6000">„wesentlich, <span style="color:#00B000">[ ,notwendig‘ ]</span>, analog"</span> zukommen. Weil Transzendentalien ebenfalls miteinander austauschbar sind, sind sie auch widerspruchsfrei, was GÖDEL mit Axiom-1 syntaktisch darstellt. Die Gültigkeit und Wahrheit, d.h. die mathematisch-logische Evidenz von Axiom-1 und Axiom-2, beruht auf der ontologischen Allgemeinheit und Gültigkeit der Transzendentalia, die GÖDEL mit Definition-2, in sein Kalkül explizit eingeführt und als Wesenseigenschaften ,bestimmt‘ hat. <span style="color:#00B000">(Definitionen werden formal-syntaktisch durch das Äquivalenzzeichen <span style="color:#4C58FF">,'''↔'''‘</span> angezeigt, gelesen als <span style="color:#FF6000">„...ist genau dann ... wenn...“</span>)</span> Die GÖDEL-Axiome und -Definitionen werden jedoch sofort <span style="color:#FF6000">„modal falsch"</span>, wenn ihre Eigenschafts-Variablen, <span style="color:#4C58FF">— ‚'''X'''‘ —</span> und <span style="color:#4C58FF">— ‚'''Y'''‘ —</span>, mit ,Nicht-Transzendentalia‘, wie z. B. ,rot‘ oder ,schwer‘, belegt werden. <span style="color:#00B000">(Dieser ,Sachverhalt‘ wird von Kritikern des GÖDEL-Systems, — zu vorschnell —, verwendet, um ihre Behauptungen zu ,belegen‘, das GÖDEL-Kalkül und seine Konsequenzen seien ,falsch‘.)</span>
Zum Überblick ''':''' <span style="color:#FF6000">„Transzendentalia“</span> sind universale, alles Seiende charakterisierende Begriffe, die über kategoriale Einteilungen hinausgehen, und sowohl in der klassischen Scholastik, als auch in der modernen Philosophie, <span style="color:#00B000">(KANT, Uwe MEIXNER<ref>vgl. die ,transzendentalen‘ Bedingungen möglicher Erkenntnisse bei KANT; und auch in der ,Axiomatischen Ontologie‘ bei Uwe MEIXNER</ref>)</span>, als Grundlage der Metaphysik und Erkenntnistheorie dienen. Sie sind die <span style="color:#FF6000">„ersten Begriffe des Seins“</span>, die jedem Ding als ,relative‘ Vollkommenheiten, <span style="color:#00B000">(Perfektionen)</span>, inhärent sind, <span style="color:#FF6000">„analogisch"</span> abgestuft, auf einen ,ultimativ‘-unbegrenzten Bezugspunkt ausgerichtet, und die sich im Denken, <span style="color:#00B000">(für uns als wahr)</span>, und in der moralischen Wertung, <span style="color:#00B000">(für uns als gut und edel)</span>, manifestieren, relativ zum ,ultimativ‘-unbegrenzten Bezugspunkt ihrer selbst. Die faktische, zusätzliche Unvollkommenheit, die sich generell in der notwendigen, und unausweichlichen Vergänglichkeit aller Dinge zeigt, ist einem ontologischen Defekt ,geschuldet‘, der stark zeitabhängig ist, d.h. der einen Anfang und ein Ende hat.
Das GÖDEL-Axiom-5 ''':''' <span style="color:#4C58FF"> — ‚'''PE'''<sub>not</sub>‘ —</span> ''':''' <span style="color:#FF6000">»''Notwendige Existenz ist eine positive Eigenschaft''«</span>, ist immer falsch, wenn es auf etwas aus der <span style="color:#FF6000">»''zufälligen Struktur der Welt''«</span> angewendet wird, wie z. B. auf einen <span style="color:#FF6000">„Tsunami“</span>, dessen ‚Existenz‘ für uns nicht ‚positiv‘ ist. KANT hat schon festgestellt ''':''' <span style="color:#FF6000">„Existenz ist keine Eigenschaft“</span>. Das gilt für alles, was <span style="color:#FF6000">„existiert“</span>. Das Axiom-5 hat nur dann seine Gültigkeit, ist nur dann ,wahr‘, wenn <span style="color:#FF6000">„Dasein“</span> <span style="color:#00B000">(Existenz)</span> und <span style="color:#FF6000">„Wesenseigenschaften“</span> <span style="color:#00B000">(Essenz)</span> in eins zusammenfallen. Bei allen Dingen, die ‚da‘ sind, ist ihr ‚Da-Sein‘ ontologisch immer verschieden zu dem ‚was‘ sie sind ''':''' zu ihrem ,Was-Sein‘. In der philosophischen Tradition, seit ARISTOTELES, wird die ontologische Identität, d.i. die <span style="color:#FF6000">„Koinzidenz“</span>, der innere Zusammenhang von ‚Sein‘ und ‚Wesen‘ allein nur dem <span style="color:#FF6000">„selbst ‚unbewegten‘ Erstbewegenden“</span> zugeschrieben, dem <span style="color:#FF6000">„<span style="font-family: Times;"><big>πρῶτον κινοῦν ἀκίνητον</big></span>“</span><span style="color:#00B000"> | ,prôton kinoûn akinêton‘</span>, von dem ARISTOTELES etwas später sagt ''':''' <span style="color:#FF6000">»''denn dies ist der Gott''«</span> und dann hinzufügt ''':''' <span style="color:#FF6000">»''so sagen wir ja''«</span>; d.h. das ist eine Interpretation aus dem Glaubenskontext des ARISTOTELES. Er war ein Gott-gläubiger Grieche. Wer an GOTT glaubt, kann das nachvollziehen. GÖDEL musste dieses Axiom-5 postulieren, sonst wäre sein Kalkül nicht aufgegangen, ohne dass er deswegen schon an GOTT glauben müsste. Er hat für sein Kalkül das ontologische Theorem von der Identität von ‚Sein‘ und ‚Wesen‘ im ‚unbewegten Erstbeweger‘ des ARISTOTELES benutzt, ohne diese Herkunft explizit referenziert zu haben. <span style="color:#00B000">(Gilt auch für Axiom-3 ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ —</span> ''':''' <span style="color:#FF6000">»''Göttlichkeit, das GOTT-Sein, das ‚Dasein‘ GOTTES, ist eine positive Wesenseigenschaft, eine Perfektion; d.h. ist das ‚Wesen‘ GOTTES ''':''' GOTT ist perfekt''«</span>)</span>. Die ontologische Identität von Sein und Wesen, Existenz und Essenz, wie auch die <span style="color:#FF6000">„Koinzidenz“</span> von Möglichkeit und Wirklichkeit, von Ursache und Wirkung, sowie auch die ontologische Einheit von <span style="color:#00B000">(Erkenntnis-)</span>Subjekt und <span style="color:#00B000">(Erkenntnis-)</span>Objekt im <span style="color:#FF6000">»''Erkennen seiner Erkenntnis''<span style="color:#00B000">[-Tätigkeit ]</span>«<span style="color:#00B000"> | </span>„<span style="font-family: Times;"><big>νοήσεως νόησις</big></span>“</span><span style="color:#00B000"> | ,noêseôs noêsis‘ <small>(<span style="font-family: Times;"><big>,''Metaphysik''‘</big></span> XII 9, 1074b34)</small></span>, und der innere Zusammenhang der <span style="color:#FF6000">„ultimativen Transzendentalia“</span>, gilt nur in der <span style="color:#FF6000">„unverursachten Letztursache"</span>, auf die ARISTOTELES bei seiner Prinzipienforschung gestoßen ist.
Es gibt verschiedene Versuche, die GÖDEL-Axiome durch sog. ,Modelle‘, relativ zu einfacheren ,Welten‘, zu verifizieren, um damit ihre relative Konsistenz nachzuweisen. Für GÖDEL aber <span style="color:#FF6000">»''sind die Axiome nur dann'' <span style="color:#00B000">[ in unserer ,realen‘ Welt ]</span> ''wahr'' <span style="color:#00B000">[ und annehmbar ]</span>«</span>, wenn sie <span style="color:#FF6000">»''unabhängig von der zufälligen Struktur der Welt'' <span style="color:#00B000">[ d.h. jeder auch nur ,möglichen‘ Welt ]</span> ''sind''«</span>. Diese Bedingung verweist jede Verifikation und jede Interpretation der Axiome auf das ,Nicht-Zufällige‘, das ,Notwendige‘, ,Absolute‘, in dem die Axiome und Definitionen des GÖDEL-Kalküls erst dadurch ihren Sinn und ihre Bedeutung bekommen, wenn sie vom ,Absoluten‘ und ,Unendlichen‘ her erklärt und verstanden werden. Damit insistiert GÖDEL auf eine genuin <span style="color:#4C58FF">,theologische‘</span> Interpretation seines Kalküls, mit der <span style="color:#4C58FF">„Theologie“</span> zum Begriff GOTT, dem unbegrenzt Unendlichen, als Verifikationskriterium. Das entspricht auch der ,methodologischen‘ Prämisse seines Kalküls. Die wichtigsten Axiome und Definitionen im GÖDEL-Kalkül sind jedoch bloße ,Annahmen‘, deren Evidenz, sowohl die ,mathematische‘ als auch die <span style="color:#4C58FF">,theologische‘</span>, erst evaluiert, d.h. ,bewiesen‘ <span style="color:#00B000">( ╞ )</span> werden muss. Das bedeutet ''':''' die logische Verifikation der Axiome und Definitionen von GOTT und seinen Vollkommenheiten kann nur Kalkül-intern durch den Aufweis ihrer Widerspruchsfreiheit erfolgen, d.i. <span style="color:#FF6000">»''unabhängig von der zufälligen Struktur der Welt''«</span>. Logisch evaluierte und verifizierte Axiome und Definitionen sind dann die ,modal‘ notwendigen, d.h. die ,transzendentalen‘ Voraussetzungen für die Ergebnisse eines Kalküls, damit seine Theoreme und Korollare in unserer ,realen‘ Welt als logisch ,wahr‘ und damit für uns auch als ,annehmbar‘ gelten können, während die Prämissen eines Kalküls, <span style="color:#00B000">(die Argument Einführung, <span style="color:#4C58FF">— '''AE:''' —</span> )</span>, nicht notwendige, und somit ,modal‘ frei gewählte ,Annahmen‘ sind. Jedoch aus diesen ,modal‘ frei gewählten, ,möglichen‘ <span style="color:#00B000">( <span style="color:#4C58FF">'''◇'''</span> )</span> Prämissen folgen mit Hilfe der ,bewiesenen‘ <span style="color:#00B000">( ╞ )</span> Axiome und Definitionen die Ergebnisse mit ,modaler‘ Notwendigkeit <span style="color:#00B000">( <span style="color:#4C58FF">'''□'''</span> )</span>.
Die Logik des GÖDEL-Systems ist eine ,Prädikatenlogik‘ zweiter Stufe, in der die Quantoren nicht nur Individuum-Variable, sondern auch Eigenschafts-Variable, <span style="color:#00B000">(als noch ,unbestimmte‘ Prädikate im Allgemeinen)</span>, binden können. Die formale Struktur des GÖDEL-Kalküls besteht aus fünf Axiomen und drei Definitionen, mit deren Hilfe in drei Beweisgängen drei Theoreme und mehrere Korollare aus seiner ,methodologischen‘ Prämisse ,regulär‘ <span style="color:#00B000">(├ )</span> abgeleitet werden können, wobei die beiden ersten Beweisgänge, mit ihren Ergebnissen, den dritten vorbereiten, in dem es dann um das Theorem ANSELMS geht. Die Prämisse des GÖDEL-Kalküls in allen Beweisgängen ist der traditionelle ,GOTT-Glaube‘, in der Formulierung speziell nach LEIBNIZ. Ein Axiom, eine Definition, zwei Theoreme und alle Korollare im GÖDEL-Kalkül sind Aussagen über <span style="color:#FF6000">„GOTT“</span>, <span style="color:#4C58FF">— ‚'''G'''‘ —</span>. Alle fünf Axiome, eine Definition und ein Theorem, <span style="color:#00B000">(und das Korollar aus Axiom-4)</span>, sind auch Aussagen über die Eigenschaft <span style="color:#FF6000">„Vollkommenheit“, „Perfektion“</span>, <span style="color:#4C58FF">— ‚'''P'''‘ —</span>, die in der <span style="color:#FF6000">»''theologischen Weltanschauung''«</span> als die Wesenseigenschaft GOTTES gilt ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ —</span>, <span style="color:#FF6000">»''GOTT ist vollkommen''«</span> bzw. <span style="color:#FF6000">»''GOTT ist das Vollkommenste der Wesen''«</span>, <span style="color:#00B000">(DESCARTES)</span>. Zwei Definitionen sind Aussagen über die allgemeinen Wesenseigenschaften aller Seienden, <span style="color:#4C58FF">— ‚'''X'''<sub>ess</sub>‘ —</span>, die Transzendentalien, die, als notwendige Existenz, <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>‘ —</span>, auch GOTT zugeordnet werden, mit der Besonderheit bei GOTT, dass sowohl alle Eigenschaften, als auch alle anderen Zuordnungen, wie Sein und Wesen, wie Ursache und Wirkung, usw., im Unendlichen, GOTT, <span style="color:#FF6000">„koinzident“</span> sind, d.h. in GOTT paarweise perspektivisch in ,eins‘ zusammenfallen, und die auch, als Transzendentalia, konvertierbar, d.h. austauschbar sind. Diese Sachverhalte machen deutlich, dass eine spezifische ,Verifikation‘ und sachgerechte ,Evaluierung‘ der GÖDEL-Axiomatik nur genuin <span style="color:#4C58FF">,theologisch‘</span> erfolgen kann. Die Evaluierung der <span style="color:#FF6000">»''mathematischen Evidenz''«</span> des GÖDEL-Systems, im Allgemeinen, muss jedoch entsprechend der Maßstäbe einer modalen Prädikatenlogik zweiter Stufe durchgeführt werden.
Das GÖDEL-Kalkül unterscheidet <span style="color:#00B000">(in Definition-3 ''':''' <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>'''x ↔ ∀X(X'''<sub>ess</sub>'''x →□∃yXy)'''‘ —</span>)</span> formal-syntaktisch zwischen der Eigenschaft ,Existenz‘, <span style="color:#4C58FF"> — ‚'''E'''‘ —</span>, die nur GOTT zugeordnet werden kann, und dem Existenz-Operator, <span style="color:#4C58FF">— ‚'''∃'''‘ —</span>, der auch allem Übrigen, das nicht GOTT ist, zugeordnet wird. Es gibt hier auch den formal-syntaktischen Unterschied zwischen der, <span style="color:#00B000">(von mir notierten, jedoch von GÖDEL schon intendierten und angesprochenen)</span>, speziellen ,Notwendigkeit‘, <span style="color:#4C58FF"> — <sub>not</sub> —</span>, die nur der Existenz GOTTES zugeordnet ist, und der modalen ,Notwendigkeit‘, <span style="color:#4C58FF">— ‚'''□'''‘ —</span>, die auf Verschiedenes bezogen werden kann. Diese Unterschiede sind Hinweise, dass GÖDEL in seiner Kalkül-Logik und -Syntax, die Außerordentlichkeit und Eigenständigkeit GOTTES berücksichtigt, der, als Schöpfer der Welt, prinzipiell und absolut <span style="color:#FF6000">»''unabhängig von der zufälligen Struktur der Welt''«</span> ist, die erst durch GOTT auch das ist, was sie ist.
===<div class="center"><span style="color:#660066">Die Genese des Kalküls</span></div>===
Wie kommt GÖDEL zu seinem Kalkül '''?''' Sein Gewährsmann war Gottfried Wilhelm LEIBNIZ, <span style="color:#00B000">(1646-1716)</span>, den er sehr schätzte. Die rekonstruierbare Genese seines Kalküls findet man in LEIBNIZ ''':''' ''<span style="font-family: Times;"><big>‚Neue Abhandlungen über den menschlichen Verstand‘</big></span>'', <span style="color:#00B000">(1704)</span>‚ ''<span style="font-family: Times;"><big>Viertes Buch, Kapitel X ''':''' ‚Von unserer Erkenntnis des Daseins Gottes‘</big></span>'', Seite 475f.
Hier der <span style="color:#00B000">[ kommentierte ]</span> Textausschnitt zum sog. ontologischen ‚Gottesbeweis‘''':'''
::<span style="color:#FF6000">»</span>''Folgendes etwa ist der Gang seines'' <span style="color:#00B000">[ d.h. ANSELMS, Erzbischof von Canterbury; 1033-1109, ]</span> ''Beweises ''':''' GOTT ist das Größte'', <span style="color:#00B000">[ ANSELM spricht vom biblischen GOTT des Glaubens, als ''':''' <span style="color:#FF6000">»''den, über dem ,Größeres‘'' | <span style="font-family: Times;"><big>‚maius‘</big></span> | ''nicht mehr gedacht werden kann''«</span> ]</span>, ''oder, wie DESCARTES es ausdrückt ''':''' das Vollkommenste der Wesen oder auch ein Wesen von äußerster Größe und Vollkommenheit'' ''':''' <span style="color:#00B000"> [ GÖDEL ''':''' </span><span style="color:#4C58FF">‚'''P'''‘</span> <span style="color:#00B000">:= ‚Perfektion‘, ‚positive Eigenschaft‘ ]</span>, ''das alle Grade derselben in sich schließt.''<span style="color:#00B000"> [ GÖDEL-Definition-1 ''':''' <span style="color:#4C58FF">— ‚'''Gx↔∀X(PX→Xx)'''‘ —</span> ''':''' <span style="color:#FF6000">»''Das Individuum'' <span style="color:#4C58FF">,'''x'''‘</span> ''ist genau dann GOTT'', <span style="color:#4C58FF">,'''G'''‘</span>, ''wenn'' <span style="color:#4C58FF">,'''x'''‘</span> ''alle Vollkommenheiten'', <span style="color:#4C58FF">,'''P'''‘</span>, ''in sich schließt''«</span>. Definition-1 bildet die traditionelle Vorstellung von GOTT ab. ]</span> ''Dies also ist der Begriff GOTTES.'' <span style="color:#00B000">[ Der Term <span style="color:#4C58FF">— ,'''G'''‘ —</span> steht hier für den biblischen ‚Begriff‘ <span style="color:#FF6000">„GOTT“</span> als ,Individuumname‘ '''!''' ]</span> ''Sehen wir nun, wie aus diesem Begriff das ‚Dasein’ folgt.''<span style="color:#00B000"> [ GÖDEL ''':''' <span style="color:#4C58FF">— ‚'''□∃xGx'''‘ —</span> ''':''' <span style="color:#FF6000">»''GOTT ist notwendig aus sich ‚da‘''«</span> ''':''' Term :10:, 1. Hauptergebnis im 3. Beweisgang. ]</span> ''Es ist etwas <u>mehr</u>, ‚da‘ zu sein, als nicht ‚da‘ zu sein, oder auch das ‚Dasein‘ fügt der Größe oder der Vollkommenheit'' <span style="color:#00B000">[ GOTTES ]</span> ''einen Grad hinzu, und wie DESCARTES es ausspricht, das ‚Dasein‘ ist selbst eine Vollkommenheit.''<span style="color:#FF6000">«</span>
<span style="color:#00B000">(Diesen Ausspruch DESCARTES übernimmt GÖDEL im Axiom-5 ''':''' <span style="color:#4C58FF"> — ‚'''PE'''<sub>not</sub>‘ —</span> ''':''' <span style="color:#FF6000">»''notwendige Existenz''</span> [ alias ‚Dasein GOTTES’ ] <span style="color:#FF6000">''ist eine positive Eigenschaft''</span> [ alias Vollkommenheit ]<span style="color:#FF6000">«</span>. Dem widerspricht KANT ''':''' <span style="color:#FF6000">„Existenz ist keine Eigenschaft“</span>, bzw. <span style="color:#FF6000">»''Sein ist kein reales Prädikat''«</span>. Das Axiom-5 ist daher nur dann ‚wahr‘, wenn ‚Wirklichsein‘ | <span style="color:#FF6000">„<span style="font-family: Times;"><big>ἐνέργεια οὖσα</big></span>“</span> | ‚enérgeia oúsa‘ | d.h. ‚Dasein‘ und ‚Wesen‘ | <span style="color:#FF6000">„<span style="font-family: Times;"><big>οὐσία</big></span>“</span> | ‚ousía‘ | — genauer ''':''' ‚Wesenseigenschaften’ —, ontologisch ,eins‘ sind, d.h. wenn <span style="color:#FF6000">„Existieren“</span> immer schon die <span style="color:#FF6000">„Wesenseigenschaft“</span> GOTTES ist. Was nach ARISTOTELES nur im <span style="color:#FF6000">„unbewegten Erstbeweger“</span> der Fall ist; bzw. mit LEIBNIZ ''':''' <span style="color:#FF6000">»''was das Privilegium der Gottheit allein ist''«</span> '''!''' Aus der ,methodologischen‘ ,Annahme‘ im 2. Beweisgang GÖDELS ''':''' <span style="color:#4C58FF">— ‚'''Gx'''‘ —</span> ''':''' <span style="color:#FF6000">»''das'' <span style="color:#4C58FF">‚'''x'''‘</span> ''steht für den GOTT der Christen''«</span>, und mit Hilfe von Axiom-3 ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ —</span> ''':''' <span style="color:#FF6000">»''Göttlichkeit ist eine positive Eigenschaft''«</span>, bzw. <span style="color:#FF6000">»''GOTT ist perfekt''«</span>, mit Axiom-4 ''':''' <span style="color:#4C58FF">— ‚'''PX → □PX'''‘ —</span> ''':''' <span style="color:#FF6000">»''Positive Eigenschaften sind notwendig aus sich, — von Natur aus —, positiv''«</span>, mit Definition-2 ''':''' <span style="color:#4C58FF">— ‚'''X'''<sub>ess</sub>'''x ↔ Xx ∧ ∀Y(Yx→ □∀y(Xy→Yy))'''‘ —</span> ''':''' <span style="color:#FF6000">»''Zum Wesen gehören notwendig auch alle Konsequenzen aus einer Wesenseigenschaft''«</span>, und mit Axiom-1 und der Definition für GOTT, folgt nach einigen logischen Umformungen das GÖDEL-Theorem-2 ''':''' <span style="color:#4C58FF">— ‚'''Gx↔G'''<sub>ess</sub>'''x'''‘ —</span> ''':''' <span style="color:#FF6000">»''Das'' <span style="color:#4C58FF">‚'''x'''‘</span> ''ist genau dann der GOTT der Christen, wenn das Wesen dieses GOTTES sein eigenes Sein ist''«</span>. Dasein und Wesen sind im Unendlichen, GOTT <span style="color:#FF6000">„koinzident“</span> ,eins‘, übereinstimmend mit dem Theorem des ARISTOTELES. Mit diesem, im Kalkül <u>ohne</u> Axiom-5 ,regulär‘ (├ ) abgeleiteten Theorem, widerlegt er KANT für den individuellen Spezialfall <span style="color:#4C58FF">‚'''G'''‘</span> := <span style="color:#FF6000">„GOTT“</span>. Nachprüfbar im Anhang ''':''' im ‚ontologischen‘ Beweis für das Basis-Theorem-2. Somit ist Axiom-5 ,wahr‘, und KANT, der <span style="color:#FF6000">»''eine Abneigung gegen das Gebet hatte''«</span> und auch <span style="color:#FF6000">»''nie zu den sonntäglichen Kirchgängern zählte''«</span><ref>Uwe SCHULTZ ''':''' ‚''<span style="font-family: Times;"><big>Immanuel Kant</big></span>''‘, Rowohlt Monographie 50659, Seite 12</ref>, hat sich hier, im Bezug auf GOTT, geirrt. <span style="color:#4C58FF">„Theologie“</span>, als Wissenschaft von GOTT, war für KANT nie eine ernstzunehmende Option. Die <span style="color:#4C58FF">„Theologie“</span>, als Wissenschaft von GOTT, die natürlich immer auch verbunden sein muss mit der täglichen ,Erfahrung‘ einer Glaubens-Praxis, im Gebet und in den Gottesdienst-Feiern des <span style="color:#4C58FF">„Theologen“</span>, und die daraus entsteht, ist eine ziemlich ,ausgereifte‘ Disziplin. Es haben sich, durch Jahrhunderte hindurch, viele gläubige und auch gescheite Menschen, schon im Judentum, und dann im Christentum, und ebenfalls im Islam, darum bemüht.)</span>
:: <span style="color:#FF6000">»</span>''Darum ist dieser Grad von Größe und Vollkommenheit oder auch diese Vollkommenheit, welche im ‚Dasein‘ besteht, in diesem höchsten, durchaus großen, ganz vollkommenen Wesen, denn sonst würde ihm ein Grad fehlen, was gegen seine Definition wäre. Und folglich ist dies höchste Wesen ‚da‘. Die Scholastiker, ohne selbst ihren'' <span style="font-family: Times;"><big>doctor angelicus</big></span> <span style="color:#00B000">[ := THOMAS von Aquin ]</span> ''auszunehmen, haben diesen Beweis verachtet'', <span style="color:#00B000">[ wie später auch Immanuel KANT ]</span>, ''und ihn als einen Paralogismus'' <span style="color:#00B000">[ := Fehlschluss ]</span> ''betrachtet, worin sie sehr unrecht gehabt haben; und DESCARTES, welcher die scholastische Philosophie im Kolleg der Jesuiten zu La Flèche lange genug studiert hatte, hat sehr recht gehabt, ihn wieder zu Ehren zu bringen. Es ist nicht ein Paralogismus, sondern ein unvollständiger Beweis'', <span style="color:#00B000">[ den GÖDEL vervollständigt hat ]</span>, ''der etwas voraussetzt, was man noch hätte beweisen sollen, um ihm mathematische Evidenz zu verleihen — nämlich, dass man dabei stillschweigend voraussetzt, diese Vorstellung des durchaus großen oder durchaus vollkommenen Wesens sei möglich und enthalte keinen Widerspruch'' <span style="color:#00B000">[ '''::''' <span style="color:#4C58FF">— '''◇''' —</span> '''::''' ,möglich‘, ,konsistent‘, ,denkbar‘; GÖDEL beweist im 1. Beweisgang Theorem-1 ''':''' <span style="color:#4C58FF">— ‚'''PX→◇∃xXx'''‘ —</span>, <span style="color:#FF6000">»''positive Eigenschaften'', <span style="color:#00B000">[ die allgemeinen Transzendentalien ]</span>, ''sind widerspruchsfrei''«</span>, und mit Axiom-3 ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ —</span>, folgt Korollar-1 ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx'''‘ —</span>, <span style="color:#FF6000">»''GOTT ist widerspruchsfrei möglich''«</span> ]</span>. ''Und es ist schon etwas, dass man durch diese Bemerkung beweist ''':''' gesetzt, dass GOTT'' <span style="color:#00B000">['' <span style="color:#FF6000">»widerspruchsfrei«</span> '']</span> '',möglich‘ ist, so ‚ist‘ er'' <span style="color:#00B000">[ ,notwendig‘ '''::''' <span style="color:#4C58FF">— '''□''' —</span> '''::''' für jede mögliche Welt auch wirklich aus sich ‚da‘ ]</span>, ''was das Privilegium der Gottheit allein ist'' ''':''' <span style="color:#00B000">[ Im 3. Beweisgang, Theorem-3 ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx→□∃xGx'''‘ —</span> ''':''' ‚ANSELMS Prinzip‘ ''':''' <span style="color:#FF6000">»''Weil GOTT widerspruchsfrei ,möglich‘ ist, darum ist auch der Glaube widerspruchsfrei, der annimmt, dass GOTT aus sich ,notwendig‘ da ist''«</span>; mit dem Korollar-3, das A. FUHRMANN konzipiert hat ''':''' <span style="color:#4C58FF">— ‚'''□∃xGx ∧ □∀y(Gy→x=y)'''‘ —</span>, <span style="color:#FF6000">»''Es gibt notwendig nur einen einzigen GOTT''«</span>. Damit ist auch der Monotheïsmus bewiesen. ]</span> ''Man hat recht, die Möglichkeit eines jeden Wesens anzunehmen und vor allem die GOTTES, bis ein anderer das Gegenteil beweist''. <span style="color:#00B000">[ Das Gegenteil besagt, dass GOTT ,unmöglich‘ ist. Hier setzt der Möglichkeitsbeweis im GÖDEL-Kalkül an, und beweist, dass diese Aussage zu einem Widerspruch führt, im Anhang ''':''' ,Widerlegung‘. ]</span> ''Somit gibt dieser metaphysische Beweis schon einen moralischen zwingenden Schluss ab, wonach wir dem gegenwärtigen Stande unserer Erkenntnisse zufolge urteilen müssen, dass GOTT ‚da‘ sei, und demgemäß handeln.'' <span style="color:#00B000">[ Aber nicht logisch zwingend '''!''' Denn die Interpretation <span style="color:#4C58FF">— ‚'''G'''‘ —</span> mit dem GOTT der Bibel, als ,methodologische‘ Kalkül-Prämisse, ist nicht zwingend, jedoch ,modal‘ möglich, <span style="color:#4C58FF">— '''◇''' —</span>, und im christlichen Glaubenskontext sinnvoll, was mit einer stimmigen <span style="color:#4C58FF">„theologischen“</span> Interpretation des GÖDEL-Kalküls gezeigt werden kann. Damit wäre dann auch die Frage beantwortet, ob das GÖDEL-System sich plausibel als eine Theorie von GOTT und seinen Eigenschaften interpretieren lässt, bzw. als eine <span style="color:#FF6000">»''axiomatische''«</span> <span style="color:#4C58FF">„Theologie“</span>, wie sie André FUHRMANN apostrophiert. Das <span style="color:#4C58FF">— ‚'''G'''‘ —</span> ist der ,Individuumname‘ für den GOTT der Bibel, — ,GOTT‘ groß geschrieben —, im monotheïstischen, christlichen Glaubenskontext, den auch LEIBNIZ teilt. Dann steht der ,Name‘ auch synonym für das ,existierende‘ Individuum, d.h. für dessen ,Existenz‘. ]</span> ''Es wäre aber doch zu wünschen, dass gescheite Männer'' <span style="color:#00B000">[ sic ! ]</span> ''den Beweis mit der Strenge einer mathematischen Evidenz vollendeten'', <span style="color:#00B000">[ was GÖDEL veranlasst hat, seine Version eines ‚ontologischen Beweises’ zu kreieren, dessen <span style="color:#FF6000">»''mathematische Evidenz''«</span> man heute mit Computerprogrammen<ref>siehe Fußnote 12</ref> schon nachgewiesen hat ]</span> ... <span style="color:#FF6000">«</span>.
Für GÖDEL war dieser Text eine intellektuelle Herausforderung, und er hat sie angenommen. Das war für GÖDEL sicher keine Glaubensangelegenheit. GOTT hat es ja auch nicht nötig, ‚bewiesen‘ zu werden. Wer <span style="color:#4C58FF">— ‚'''G'''‘ —</span> z. B. mit dem sog. ‚Urknall‘ gleich setzt, macht die <span style="color:#FF6000">»''zufällige Struktur der Welt''«</span> im ‚Urknall‘, <span style="color:#00B000">(pantheistisch)</span> zu einem ,Gott‘, was GÖDEL dezidiert für sein Kalkül ausgeschlossen haben wollte.
Kurt GÖDEL schreibt 1961 in einem Brief, in Anlehnung an den obigen Text ''':'''
::<span style="color:#FF6000">»...''ich glaube, schon heute dürfte es möglich sein, rein verstandesmäßig ''<span style="color:#00B000">[ sic '''!''' ]</span>, ''(ohne sich auf den Glauben an irgendeine Religion zu stützen) einzusehen, dass die theologische Weltanschauung'', <span style="color:#00B000">[ dass es GOTT gibt ]</span>, ''mit allen bekannten Tatsachen'', <span style="color:#00B000">[ z. B. mit den Maßstäben einer modernen Logik ]</span>, ''durchaus vereinbar ist. Das hat schon vor 250 Jahren der berühmte Philosoph und Mathematiker LEIBNIZ versucht''.«</span><ref>Zitiert nach SCHIMANOVICH-GALIDESCU, M.-E. ''':''' ‚''<span style="font-family: Times;"><big>Princeton–Wien 1946–1966. Briefe an die Mutter</big></span>''‘, in ‚''<span style="font-family: Times;"><big>Kurt Gödel – Leben und Werk</big></span>''‘, Hg. B.BULDT et alia, Wien (HÖLDER–PICHLER–TEMSKY), 2001, Band 1</ref>
===<div class="center"><span style="color:#660066">Die Interpretation des Kalküls</span></div>===
Wenn man sich das GÖDEL-Kalkül ansieht, wie es heute formalisiert vorliegt, stellt sich die Frage ''':''' <span style="color:#FF6000">„Lässt sich dieses System plausibel als eine Theorie von GOTT <span style="color:#00B000">(als eine ‚Rede von GOTT’ := <span style="color:#4C58FF">,Theologie’</span>)</span> und seiner Eigenschaften verstehen '''?''' “ — „Ist hier eine genuin <span style="color:#4C58FF">,theologische’</span> Interpretation möglich '''?''' “</span> Seine Herkunft aus der intellektuellen Auseinandersetzung des Logikers GÖDEL mit dem GOTT-gläubigen Philosophen LEIBNIZ und dem christlichen Theologen und Erzbischof ANSELM rechtfertigt diese Frage. Die <span style="color:#FF6000">„mathematische Evidenz“</span> des GÖDEL-Formalismus, <span style="color:#00B000">(im Anhang nachgestellt)</span>, ist allgemein anerkannt, <span style="color:#00B000">(Vorbehalte dagegen gibt es nur bei der Interpretation seiner Syntax, d.h. ob die Axiome und Definitionen, wie GÖDEL sie konzipiert hat, bzw. ob die Theoreme und Korollare, die aus diesen Axiomen ,regulär’ (├ ) abgeleitet werden, auch in unserer realen Welt ,wahr’ und ,annehmbar’ sind)</span>. Die <span style="color:#FF6000">„theologische Evidenz“</span> des GÖDEL-Systems wird durch eine ,Verankerung’ der Axiome und Definitionen in den <span style="color:#4C58FF">,theologisch’</span>-philosophischen Diskurs über GOTT evaluiert, der schon seit zweieinhalbtausend Jahren läuft. In diesen zweieinhalbtausend Jahren hat sich, — gegen ARISTOTELES und die antike Philosophie —, die Erkenntnis durchgesetzt, dass GOTT <span style="color:#FF6000">»''unabhängig''«</span> von der <span style="color:#FF6000">»''zufälligen''«</span> Raum-Zeit-Struktur unserer vergänglichen Welt ist. In meiner Darstellung des GÖDEL-Kalküls folge ich, <span style="color:#00B000">(im Unterschied zum Autographen GÖDELS, vom 10. Feb 1970)</span>, in der Axiom-Formulierung und -Nummerierung, in der Syntax, und in der Beweis-Struktur, der Arbeit von André FUHRMANN ''':''' ''<span style="font-family: Times;"><big>‚Existenz und Notwendigkeit. Kurt Gödels axiomatische Theologie‘</big></span>'' in ''<span style="font-family: Times;"><big>‚Logik in der Philosophie‘</big></span>'' Hg. SCHROEDER-HEISTER, SPOHN und OLSSON, 2005, Synchron, Heidelberg, Seite 349–374. <span style="color:#00B000">(Die tiefer gestellte Notation der spezifischen ,Eigenschaft‘ einer Eigenschaft ist meine Ergänzung zur formalen Syntax, z. B. ''':''' <span style="color:#4C58FF"> — ‚'''G'''<sub>ess</sub>'''x'''‘ — </span>, angeregt durch die indizierende Schreibweise GÖDELS ''':''' <span style="color:#4C58FF">— ,'''G''' Ess. '''x'''’ —</span> ''':''' <span style="color:#FF6000">»''das'' <span style="color:#4C58FF">,'''x'''’</span> ''ist wesentlich göttlich''«</span>, m.a.W. <span style="color:#FF6000">»''das Individuum'' <span style="color:#4C58FF">,'''x'''’</span> ''hat die Wesenseigenschaft, GOTT zu sein''«</span>.)</span> Die Erkenntnisse zur Straffung und Präzisierung der GÖDEL-Syntax, <span style="color:#00B000">(besonders im Möglichkeitsbeweis)</span>, stammen aus der Arbeit von Günther J. WIRSCHING ''':''' ,''<span style="font-family: Times;"><big>Der Gödelsche Gottesbeweis</big></span>''‘, im Web <ref>https://edoc.ku.de/id/eprint/10243/1/OntBw.pdf</ref>. <span style="color:#00B000">(Auch der Hinweis auf AVICENNA kommt von WIRSCHING.)</span> Die Zitate von THOMAS von Aquin´s Stellungnahme zum Theorem ANSELMS, und von Georg Wilhelm Friedrich HEGEL zur Immanuel KANTS Ablehnung des Theorem ANSELMS, befinden sich in Franz SCHUPP, ,<span style="font-family: Times;"><big>''Geschichte der Philosophie im Überblick''</big></span>‘, Band 2 ‚<span style="font-family: Times;"><big>''Christliche Antike, Mittelalter''</big></span>‘, Hamburg 2003, Seite 168 und Seite 170.
Meines Erachtens ist der entscheidende Ansatzpunkt einer <span style="color:#4C58FF">„theologischen“</span> Interpretation das GÖDEL-Axiom-5 ''':''' <span style="color:#4C58FF">— ‚'''PE'''<sub>not</sub>‘ —</span>, <span style="color:#FF6000">»''notwendige Existenz ist eine positive Eigenschaft''«</span>.
‚Frei‘ nach KANT ‚formuliere‘ ich ‚kurz‘ ''':''' <span style="color:#FF6000">„Existenz ist keine Eigenschaft“</span>. Hier die Positionen KANTS zum Thema ‚Existenz‘ und ‚Eigenschaften‘ ''':'''
::<span style="color:#FF6000">»</span>''… unbeschadet der wirklichen Existenz äußerer Dinge'', <span style="color:#00B000">[ kann man ]</span> ''von einer Menge ihrer Prädikate'', <span style="color:#00B000">[ d.h. Eigenschaften ],</span> ''sagen'' … ''':''' ''sie gehöreten nicht zu diesen ‚Dingen an sich selbst‘, sondern nur zu ihren Erscheinungen, und hätten außer unserer Vorstellung'' <span style="color:#00B000">[ ihrer, als ,wirklich‘ gedachten Erscheinung, ]</span> ''keine eigene Existenz, … weil ich finde, dass … '''alle Eigenschaften, die die Anschauung eines Körpers ausmachen''', bloß zu seiner Erscheinung gehören; denn die Existenz des Dinges, was erscheint, wird dadurch nicht … aufgehoben, sondern nur gezeigt, dass wir es'', <span style="color:#00B000">[ das Ding ]</span>, ''wie es ‚an sich selbst‘ sei'', <span style="color:#00B000">[ d.h. existiert ]</span>, ''durch Sinne gar nicht erkennen können''.<span style="color:#FF6000">«</span><ref>vgl. ‚''<span style="font-family: Times;"><big>Prolegomena zu einer jeden künftigen Metaphysik, die als Wissenschaft wird auftreten können</big></span>''‘, Seite 289; https://www.korpora.org/kant/aa04/289.html</ref> <span style="color:#00B000"><small>(Hervorhebung durch KANT.)</small> [ Seine Prädikate, d.h. Eigenschaften, jedoch können wir mit unseren Sinnen ,anschauen‘, aber nur in Kombination mit unserer Vorstellung ihrer, als ,wirklich‘ gedachten Erscheinung, vermittelt durch den sog. ,transzendentalen Schematismus‘ unserer Einbildungskraft ''':'''</span> <span style="color:#FF6000">»</span>''Eine verborgene Kunst in den Tiefen der menschlichen Seele''<span style="color:#FF6000">«</span>, <span style="color:#00B000">jedoch auch eines der ,dunkelsten‘ Kapitel in der K.d.r.V., bedingt durch KANTS Konzept von ,Wirklichkeit‘, bzw. ,Sein‘. ]</span>
Mit anderen Worten, man kann die ‚Existenz‘, bzw. das ‚Sein‘ der Dinge, <span style="color:#00B000">(das ‚Ding an sich’ bei KANT)</span>, nicht unter dem Mikroskop finden. Die ‚Existenz‘ bzw. das ‚Sein‘ ist keine sinnlich registrierbare ‚Eigenschaft‘ z. B. des rekonstruierten ‚Stadt-Schlosses‘ in Berlin. <span style="color:#00B000">(‚Sein‘ ist kein reales ‚Prädikat‘.)</span> Dafür haben wir andere Fähigkeiten ''':''' Ich kann seine ‚Existenz‘ mit meinem Verstand einsehen, weil auch ich selbst ‚existiere‘. Seine ‚Ansicht‘, wie ‚gefällig‘ es ist, und auch weitere ‚Eigenschaften‘, die mir auffallen, kann ich mit einem Handy-Foto dokumentieren. Diese ‚Eigenschaften‘ sind nicht die Ursache, dass das ‚Berliner Schloss‘ existiert. Wohl aber die Rekonstruktion dieses Schlosses ist die ‚Ursache‘, dass es ,existiert‘, und jetzt so aussieht. Insofern ist ‚Existenz‘ keine ‚Eigenschaft‘, sondern die ‚Existenz‘ des Dinges ist die Voraussetzung, der ‚Grund‘, dass ich die ‚Eigenschaften‘ des Dinges mit meinen Sinnen feststellen kann.
In einer Auseinandersetzung mit CARTESIUS schreibt KANT, philosophisch ‚tiefgründig‘ und logisch ‚exakt‘, über dessen <span style="color:#FF6000"><span style="font-family: Times;"><big>„Cogito, ergo sum“</big></span></span> ''':'''
::<span style="color:#FF6000">»</span>''Das ‚Ich denke‘ ist ein empirischer Satz, und hält den Satz ‚Ich existiere‘ in sich. Ich kann aber nicht sagen ''':''' ‚Alles, was denkt, existiert‘; denn da würde die Eigenschaft des Denkens'', <span style="color:#00B000">[ eine essentielle Eigenschaft ]</span>, ''alle Wesen, die sie besitzen, zu notwendigen'' <span style="color:#00B000">[ d.h. notwendig existierenden ]</span> ''Wesen machen''. <span style="color:#00B000">[ Was allein nur von GOTT ausgesagt werden kann; mit AVICENNA, als anerkannter ARISTOTELES-Kommentator ''':''' <span style="color:#FF6000">»''GOTT ist das einzige Sein, bei dem Essenz'' <span style="color:#00B000">[ ‚Wesenseigenschaften‘ ]</span> ''und Existenz'' <span style="color:#00B000">[ ‚Dasein‘ ]</span> ''nicht zu trennen sind und das daher notwendig an sich da ist''«, <span style="color:#00B000">— konform mit GÖDEL ''':'''</span> »''notwendige Existenz ist eine positive'' <span style="color:#00B000">[ essentielle ]</span> ''Eigenschaft''«</span> ].</span> ''Daher kann meine Existenz auch nicht aus dem Satz, ‚Ich denke‘, als'' <span style="color:#00B000">[ logisch ]</span> ''gefolgert angesehen werden, wie CARTESIUS dafür hielt (weil sonst der Obersatz : ‚Alles, was denkt, existiert‘, vorausgehen müsste), sondern ist mit ihm identisch.'' <span style="color:#00B000">[ Eine einfache Schlussfolgerung ''':''' meine ‚Existenz‘ ist auch nicht von meiner ‚Eigenschaft‘ Denken ‚verursacht‘. ,Existenz‘ ist nicht bloß ein ,Gedanke‘ von mir. ]</span><span style="color:#FF6000">«</span> <span style="color:#00B000">(Aus der Anmerkung 41 zu den ‚''<span style="font-family: Times;"><big>Paralogismen der reinen Vernunft</big></span>''‘, in ‚''<span style="font-family: Times;"><big>Kritik der reinen Vernunft</big></span>''‘, Seite 275,<ref>https://korpora.org/kant/aa03/275.html</ref> mit meinem Einschub des AVICENNA-Zitat aus Wikipedia.<ref>{{w|Avicenna#Metaphysik}}</ref>)</span>
Mit anderen Worten ''':''' <span style="color:#FF6000">„Die Eigenschaft, dass ich denken kann, ist nicht die Ursache meiner ‚Existenz‘“</span>, sondern, <span style="color:#FF6000">„Die Liebe meiner Eltern und ihre Entscheidung füreinander ist die Ursache meiner ‚Existenz‘. Daher ‚bin’ ich. Und weil ich ein Mensch ‚bin‘, kann ich denken.“</span> Auch mit diesen Anmerkungen ist leicht einsehbar, dass ‚Existenz‘ keine ‚Eigenschaft‘ ist — außer bei GOTT. In GOTT ist ‚Dasein‘ die ‚Wesenseigenschaft‘ GOTTES, d.h. ‚Dasein‘ und ‚Wesen‘ sind in GOTT untrennbar verbunden; sind <span style="color:#FF6000">„koinzident“</span> ‚eins‘. Das ist die Einzigartigkeit im Wesen GOTTES, dass GOTT immer schon ‚da‘ ist. Die Frage nach dem ‚Wesen‘ GOTTES lautet ''':''' <span style="color:#FF6000">„Was bist du ? “</span>/<span style="color:#CC66FF">„Wer bist Du ? “</span> Antwort, Exodus 3,14 ''':''' <span style="color:#CC66FF">»''Ich bin der ‚Ich-Bin-Da‘'' <span style="color:#00B000">[ für euch und für immer ]</span>«.</span> Weil GOTT für uns immer schon ‚da‘ ist, — <span style="color:#CC66FF">„von Ewigkeit zu Ewigkeit“</span> —, hat GOTT es nicht nötig, ‚bewiesen‘ zu werden. <span style="color:#00B000">(In der Mathematik ist ein ‚Satz‘ erst dann ‚wahr‘ und ‚existent‘, wenn er bewiesen ist. Bei GOTT ist es jedoch nicht so ''':''' GOTTES ‚Existenz‘ ist nicht erst dann ‚wahr‘, wenn seine ‚Existenz‘ von uns ‚bewiesen‘ ist. Sein ‚Dasein‘ ist jedem unserer ‚Beweisversuche‘ immer schon voraus. Der Zugang zu GOTT ist nicht der ,Beweis‘, sondern der ,Glaube‘. Wer an GOTT glauben ,will‘, dem antwortet GOTT. Wer nicht an GOTT glauben ,will‘, dessen Entscheidung respektiert GOTT, und drängt sich nicht auf. Die Glaubens-Entscheidung hat jedoch für jeden Menschen eine existenzielle Konsequenz ''':''' <span style="color:#CC66FF">»''Wer glaubt und sich taufen lässt, wird gerettet; wer aber nicht glaubt'', <span style="color:#00B000">[ und diese Entscheidung auch im Augenblick der ,Wahrheit‘, im Tod, in der sog. ,Endentscheidung‘, nicht widerruft ]</span>, ''wird verurteilt werden''«,</span> <small>({{Bibel | Markus Evangelium |16|16|EU}})</small>. Das Urteil lautet ''':''' <span style="color:#CC66FF">»''zweiter Tod ''':''' der Feuersee''«</span>, <small>({{Bibel | Offenbarung |20|14f|EU}})</small>, ohne Berufungsmöglichkeit. <span style="color:#CC66FF">»''Ohne Glauben aber ist es unmöglich, Gott zu gefallen; denn wer zu Gott kommen will, muss glauben, dass er ist und dass er denen, die ihn suchen, ihren Lohn geben wird''«.</span> <small>({{Bibel | Hebräer Brief |11|6|LUT}})</small>)</span>
Das GÖDEL-Axiom-5 ist m.E. der entscheidende Ansatzpunkt einer stimmigen <span style="color:#4C58FF">„theologischen“</span> Interpretation der GÖDEL-Axiomatik.
===<div class="center"><span style="color:#660066">Das Kalkül ist kein Existenz-Beweis für GOTT</span></div>===
Die allgemeine <span style="color:#FF6000">»''mathematische Evidenz''«</span> des GÖDEL-Formalismus, d.h. seine ‚Schlusskraft‘, ist von kompetenten Leuten<ref>„GÖDELS Argumentationskette ist nachweisbar korrekt – so viel hat der Computer nach Ansicht der Wissenschaftler Christoph BENZMÜLLER und Bruno WOLTZENLOGEL-PALEO nun gezeigt;“ vgl. https://www.fu-berlin.de/presse/informationen/fup/2013/fup_13_308/index.html</ref> schon festgestellt worden, <span style="color:#00B000">(im Anhang ‚nachrechenbar‘ mit den Regeln und Gesetzen einer modalen Prädikatenlogik 2. Stufe)</span>. Das GÖDEL-Kalkül ist jedoch kein ‚moderner‘<span style="color:#FF6000">„Existenz-Beweis“</span> für GOTT, wofür es gehalten oder meistens bezweifelt wird, sondern setzt, <span style="color:#00B000">(theoretisch methodisch)</span>, den <span style="color:#FF6000">„Glauben an die Existenz GOTTES“</span> schon voraus, ohne ihn zu hinterfragen. Das <span style="color:#FF6000">„Dasein“</span> bzw. die <span style="color:#FF6000">„Existenz“</span> GOTTES wird mit der Definition-1 für <span style="color:#4C58FF">— ‚'''Gx'''‘ —</span>, bzw. mit dem Axiom-3 ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ — </span>, im Kalkül ‚definitorisch‘ bzw. ‚axiomatisch‘ als Kalkül-,Annahme‘, als <span style="color:#FF6000">„Prämisse“</span>, eingeführt, unter der Voraussetzung, dass die ‚Eigenschaft‘ <span style="color:#4C58FF">‚'''G'''‘ := </span><span style="color:#FF6000">„Göttlichkeit“</span> <span style="color:#00B000">(Essenz)</span> und das <span style="color:#FF6000">„Dasein GOTTES“</span>, <span style="color:#4C58FF">— ‚'''G'''‘ —</span> <span style="color:#00B000">(Existenz)</span>, ontologisch ‚identisch‘, genauer ''':''' <span style="color:#FF6000">„koinzident“</span> sind, was GÖDEL im Axiom-5 definitiv für sein System vorschreibt ''':''' <span style="color:#4C58FF">— ‚'''PE'''<sub>not</sub>‘ — </span> ''':''' <span style="color:#FF6000">»''notwendige Existenz ist eine positive Eigenschaft''«</span>. Das <span style="color:#FF6000">„Dasein GOTTES“</span> ist faktisch äquivalent zur <span style="color:#FF6000">„notwendigen Existenz als GOTT“</span>; und <span style="color:#FF6000">„Göttlichkeit“</span> ist die <span style="color:#FF6000">„positive Eigenschaft in GOTT“</span>. Beides ist nach Axiom-5 ‚identisch‘, d.h. dem ‚Sein nach‘ dasselbe, und daher konvertierbar. Beide, <span style="color:#00B000">(sowohl die Essenz, als auch die Existenz GOTTES)</span>, werden daher auch mit demselben Term ''':''' <span style="color:#4C58FF">— ‚'''G'''‘ —</span> im Kalkül dargestellt. Der traditionelle, christliche ,GOTT-Glaube‘ wird ,systematisch‘ mit diesem Term <span style="color:#4C58FF">‚'''G'''‘ := </span> <span style="color:#FF6000">„GOTT“ <span style="color:#00B000">|</span> „göttlich“</span>, im 2. Beweisgang, dem Basisbeweis, und im 3. Beweisgang für das Theorem ANSELMS, jeweils als ,methodologische‘ Prämisse :01: <span style="color:#4C58FF">— ‚'''Gx'''‘ —</span> regulär <span style="color:#00B000">( ├ )</span> und explizit eingeführt ''':''' <span style="color:#FF6000">»''Das'' <span style="color:#4C58FF">,'''x'''‘</span> ''steht für den GOTT'' <span style="color:#4C58FF">‚'''G'''‘</span> ''der Christen''«</span>. Das ist die ,modal‘-frei gewählte, <span style="color:#4C58FF">„theologische“</span> Kalkül-,Annahme‘, <span style="color:#00B000">(als ,Argument-Einführung‘ := <span style="color:#4C58FF">‚'''AE:'''‘</span> )</span>, und wird dann mit Definition-1 näher ,bestimmt‘ ''':''' <span style="color:#4C58FF">— ‚'''Gx↔∀X(PX→Xx)'''‘ — </span> ''':''' <span style="color:#FF6000"> »''Das'' <span style="color:#4C58FF">,'''x'''‘</span> ''steht genau dann für ‚GOTT‘'' <span style="color:#00B000">|</span> ''‚göttlich‘'', <span style="color:#4C58FF">‚'''G'''‘</span>, ''wenn'' <span style="color:#4C58FF">,'''x'''‘</span> ''alle positiven Eigenschaften, bzw. Vollkommenheiten'', <span style="color:#4C58FF">‚'''PX'''‘</span>, ''hat''«</span>, entsprechend dem ‚Quelltext‘ bei LEIBNIZ. <span style="color:#00B000">(Das ,postulierte‘ Axiom-3 ''':''' <span style="color:#4C58FF"> — ‚'''PG'''‘ —</span>, wird standardmäßig gelesen als <span style="color:#FF6000">»''Göttlichkeit ist eine positive Eigenschaft''«</span>, hat aber auch die alternative Leseart ''':''' <span style="color:#FF6000">»''GOTT ist perfekt d.h. vollkommen''«</span>, was <span style="color:#4C58FF">„theologisch“</span> auch richtig ist; mit <span style="color:#4C58FF">‚'''P'''‘ </span> := <span style="color:#FF6000">„Perfektion“/„Vollkommenheit“</span> ist dann die Summe aller <span style="color:#FF6000">„positiven Eigenschaften“</span>.)</span> Mit Axiom-3, — in dieser <span style="color:#4C58FF">„theologischen“</span> Leseart —, ist der ‚Wenn-Satz‘ in Definition-1 ‚aufgelöst‘ ''':''' <span style="color:#FF6000">»''GOTT hat alle positiven Eigenschaften, weil er ‚perfekt‘ ist''«</span>.
In Definition-3 ''':''' <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>'''x ↔ ∀X(X'''<sub>ess</sub>'''x →□∃yXy)'''‘ —</span>, wird die ,für uns‘ <span style="color:#FF6000">„notwendige Existenz“</span> ''':''' <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>‘ —, </span> durch die ,aus sich‘ <span style="color:#FF6000">„notwendig“</span> <span style="color:#4C58FF">— '''□''' —</span> instanziierten <span style="color:#FF6000">„Wesenseigenschaften“</span>, <span style="color:#4C58FF">— ‚'''X'''<sub>ess</sub>‘ —</span>, <span style="color:#00B000">(als zu den Transzendentalia gehörig)</span>, ,bestimmt‘. Das GÖDEL-Kalkül setzt sowohl in Definition-3 als auch im Axiom-5 das ,Theorem‘ des ARISTOTELES von der ,ontologischen Identität‘, d.i. die ,Koinzidenz‘ von <span style="color:#FF6000">„Dasein“</span>, <span style="color:#00B000">(Existenz)</span> und <span style="color:#FF6000">„Wesenseigenschaften“</span>, <span style="color:#00B000">(Essenz)</span> im prinzipiell <span style="color:#FF6000">„unbewegten Erstbewegenden“</span> voraus. Ohne diese Annahme bzw. ohne Axiom-5, würde das GÖDEL-Kalkül nicht ‚funktionieren‘. Das GÖDEL-Theorem-2.1 ''':''' <span style="color:#4C58FF">— ‚'''Gx→G'''<sub>ess</sub>'''x'''‘ —</span>, kann unter dieser Voraussetzung dann, <span style="color:#4C58FF">„theologisch“</span> richtig und eindeutig, so gelesen werden ''':''' <span style="color:#FF6000">»''Wenn'' <span style="color:#4C58FF">‚'''x'''‘</span> ''für den GOTT der Christen'', <span style="color:#4C58FF">‚'''G'''‘</span>, ''als Individuum steht, dann ist GOTT-Sein'', <span style="color:#4C58FF">‚'''G'''‘</span>, <span style="color:#00B000">(,Existenz‘)</span>, ''die Wesenseigenschaft'', <span style="color:#4C58FF">—<sub>ess</sub>—, </span> <span style="color:#00B000">(,Essenz‘)</span>, ''dieses GOTTES'' <span style="color:#4C58FF">‚'''x'''‘</span>«</span><ref>vgl. z.B. THOMAS von Aquin ''':''' ,''<span style="font-family: Times;"><big>De Ente et Essentia</big></span>''’, Kapitel 5 ''':''' „Deus, cuius essentia est ipsummet suum esse“ ''':''' „GOTT, dessen Wesen sein eigenes Sein ist“.</ref>, statt der <span style="color:#4C58FF">„theologisch“</span> unrichtigen Lesearten in der Wikipedia ''':''' <span style="color:#FF6000">»''Göttlich ist eine essentielle Eigenschaft jedes göttlichen Wesens''«</span><ref>{{w|Gottesbeweis#Kurt_Gödel|Gottesbeweis 2.1.2, Theorem 2}}; Version vom 10.09.2025</ref>, oder bei Christoph BENZMÜLLER et alia, im sog. ,Theorembeweiser‘ ''':''' <span style="color:#FF6000">»''Gottähnlich zu sein ist eine Essenz von jeder gottähnlichen Entität''«</span><ref>[https://www.fu-berlin.de/presse/informationen/fup/2013/fup_13_308/index.html ‚Gödels „Gottesbeweis“ bestätigt’, Theorem 2]</ref>, mit der suggestiven Annahme, es gäbe polytheïstisch, mehrere ,göttliche Wesen‘, bzw. ,gottähnliche Entitäten‘, was der monotheïstischen, abendländischen Tradition, bzw. dem <span style="color:#4C58FF">„theologischen“</span> Theorem von der ,Unvergleichlichkeit‘ und ,Einzigartigkeit‘ GOTTES widerspricht, das im GÖDEL-Kalkül mit dem FUHRMANN-Korollar-3 bestätigt wird. <span style="color:#00B000">(Die Interpretation <span style="color:#4C58FF">‚'''G'''‘ :=</span> <span style="color:#FF6000">„GOTT“</span>, als ,Individuumname‘, ist synonym zum <span style="color:#FF6000">„Dasein <span style="color:#00B000">(Existenz)</span> GOTTES“</span>, und äquivalent zur ‚positiven Eigenschaft‘ <span style="color:#FF6000">„Göttlichkeit“</span>, alias <span style="color:#FF6000">„göttlich zu sein“</span> = <span style="color:#FF6000">„GOTT zu sein“</span> = <span style="color:#FF6000">„GOTT-Sein“</span>; und mit dem GÖDEL-Term ''':''' <span style="color:#4C58FF">‚'''G'''<sub>ess</sub>‘ :=</span> <span style="color:#FF6000">„das Wesen <span style="color:#00B000">(Essenz)</span> GOTTES“</span>.)</span>
<div class="center"><span style="color:#FF6000"><span style="color:#4C58FF">‚'''G'''‘ :=</span> „'''G'''öttlichkeit“ <span style="color:#4C58FF">↔</span> „'''G'''OTT“ <span style="color:#4C58FF">↔</span> „'''G'''OTT-Sein“</span> </div>
Die Rechtfertigung für diese <span style="color:#4C58FF">„theologische“</span> Dreifach-Äquivalenz für <span style="color:#4C58FF">— ‚'''G'''‘ —</span>, im GÖDEL-Kalkül, gibt Axiom-5 ''':''' <span style="color:#4C58FF">— ‚'''PE'''<sub>not</sub>‘ — </span> ''':''' <span style="color:#FF6000">»''die positive <u>Eigenschaft</u>'', <span style="color:#4C58FF">— ‚'''P'''‘ —</span>, ''Göttlichkeit'', <span style="color:#4C58FF">— ‚'''G'''‘ —</span>, ''äquivalent zu GOTT als Individuum-Name'', <span style="color:#4C58FF">— ‚'''G'''‘ —</span>, ''ist auch äquivalent zum Dasein GOTTES'', <span style="color:#4C58FF">— ‚'''G'''‘ —</span>, ''gleichbedeutend mit notwendiger <u>Existenz</u>'', <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>‘ —</span>'', dem <u>Sein</u> GOTTES für uns''«</span>. Hier hat GÖDEL explizit <span style="color:#FF6000">„Eigenschaft“</span> mit <span style="color:#FF6000">„Existenz“</span> bzw. <span style="color:#FF6000">„Sein“</span> gleichgesetzt; <span style="color:#00B000">(was jedoch nach KANT für alles, was in unserer Welt ‚existiert‘, bzw. für alles, was zur <span style="color:#FF6000">»''zufälligen Struktur der Welt''«</span> gehört, wie GÖDEL selbst sagt, in jedem Fall ‚unstatthaft‘ ist ''':''' <span style="color:#FF6000">„Existenz ist keine Eigenschaft“</span>, bzw. <span style="color:#FF6000">»''Sein ist kein reales Prädikat''«</span>)</span>. Jedoch wegen dieser ‚Gleichsetzung‘, die einzig und allein, der aristotelisch-<span style="color:#4C58FF">,theologischen‘</span> Tradition entsprechend, singulär nur in GOTT ‚statthaft‘ ist, kann jetzt die ‚positive Eigenschaft‘ <span style="color:#00B000">(Essenz)</span> <span style="color:#FF6000">„Göttlichkeit“</span> <span style="color:#00B000">(ontologisch korrekt)</span> gelesen werden als <span style="color:#FF6000">„das, was GOTT zu dem macht, ‚was‘ GOTT an sich selbst ist“</span>, nämlich zu seinem <span style="color:#FF6000">„GOTT-Sein“</span> <span style="color:#00B000">(Existenz)</span>, zu seinem <span style="color:#FF6000">„Dasein als GOTT“</span>; zur Tatsache, dass <span style="color:#FF6000">„GOTT GOTT ist“</span>, d.h. dass <span style="color:#FF6000">„GOTT als GOTT ‚da‘ ist“</span>. Das ist, <span style="color:#00B000">(und da folgt ARISTOTELES seinem Lehrer PLATO)</span>, nach traditioneller Auslegung, die übliche, ontologische Funktion des ‚Wesens‘<span style="color:#00B000"> | <span style="color:#FF6000">„<span style="font-family: Times;"><big>οὐσία</big></span>“</span> | ‚ousía‘ |</span> eines Seienden ''':''' es ‚macht‘ das Seiende zu dem, ‚was‘ es ist; es ist die ‚Ursache‘ dafür, dass das Seiende, das ‚ist‘, ‚was‘ es ist | ‚Was-Sein‘ — ‚Wesen‘. <span style="color:#00B000">(ARISTOTELES lokalisiert jedoch das ,Wesen‘ im Seienden, im Gegensatz zu PLATO, der das ,Wesen‘, — ,getrennt‘ vom Seienden —, in den allgemeinen ,Ideen‘ lokalisiert.)</span>
Da aber in ‚Gott‘, <span style="color:#00B000">(dem <span style="color:#FF6000">„unbewegten, ‚unverursachten‘ Erstbeweger“</span>)</span>, Prozesshaftes, ‚Ursächliches‘ auszuschließen ist, ist die übliche prozesshafte, ‚ursächliche‘ Funktion von <span style="color:#FF6000">„<span style="font-family: Times;"><big>οὐσία</big></span>“</span><span style="color:#00B000"> | ‚ousía‘ |</span> ,Wesen‘ im <span style="color:#FF6000">„Erstbewegenden“</span> nach ARISTOTELES, sozusagen, schon ‚zum Abschluss‘ gekommen, schon ‚verwirklicht‘, — <span style="color:#FF6000">„<span style="font-family: Times;"><big>ἐν-έργεια οὖσα</big></span>“</span><span style="color:#00B000"> | ‚en-érgeia oúsa‘</span> —, schon ‚ins-Werk‘ gesetzt; <span style="color:#00B000">(<span style="color:#FF6000">„<span style="font-family: Times;"><big>τὸ ἔργον</big></span>“</span> | ‚to érgon‘ | ‚das Werk‘; <span style="color:#FF6000">„<span style="font-family: Times;"><big>ἐνέργεια</big></span>“</span> | ‚enérgeia‘ | ,Wirksamkeit‘, ,Wirklichkeit‘, ,Aktualität‘, ,Energie‘; und <span style="color:#FF6000">„<span style="font-family: Times;"><big>οὖσα</big></span>“</span> | ,oúsa‘ | feminin Nominativ Singular von <span style="color:#FF6000">„<span style="font-family: Times;"><big>ὤν</big></span>“</span> | ‚ón‘ | ‚seiend‘)</span>. Sein ,Wesen‘ ist im ,Dasein‘ vollendet, ist ,wirkliches, verwirklichendes Sein‘, ‚seiende Aktualität‘, <span style="color:#FF6000">„<span style="font-family: Times;"><big>actus purus</big></span>“</span> ''':''' sein Wesen ist ‚reine Tätigkeit‘, ,reine verwirklichende Gegenwärtigkeit‘, d.h. ,existent‘, reine ,Aktualität‘, ohne jede prozesshafte ‚Potenzialität‘. Aus der wichtigen und richtigen Erkenntnis, dass GOTT <span style="color:#FF6000">»''unabhängig von der zufälligen'' <span style="color:#00B000">[ Raum-Zeit-]</span>''Struktur unserer Welt''«</span> ist, folgt mit der ontologischen Identität von ,Dasein‘ und ,Wesen‘ in GOTT ''':''' Der zeitlos ewige GOTT ist <span style="color:#FF6000">»''notwendig aus sich'' <span style="color:#00B000">(von Natur aus)</span> ''immer schon da''«</span>, m.a.W. ist <span style="color:#FF6000">„zeitlos-ursprungslos“</span>. Insofern ist <span style="color:#FF6000">„Göttlichkeit“</span> die <span style="color:#FF6000">„Wesenseigenschaft“</span>, die im <span style="color:#FF6000">„Dasein GOTTES“</span> d.h. in <span style="color:#FF6000">„GOTT“</span>, schon ihr ‚Ziel‘, ihre Vollendung, — <span style="color:#FF6000">„Perfektion“</span>, Axiom-3 ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ —</span>, erreicht hat. GOTT ist <span style="color:#FF6000">»''vollkommen''«</span> und darum auch <span style="color:#FF6000">»''notwendig für uns immer schon ‚da‘''«</span> ''':''' <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>'''x'''‘ — </span>. GOTT ist in seinem ‚zeitlosen Wesen‘ <span style="color:#FF6000">„unverursacht“</span>, da er <span style="color:#FF6000">»''notwendig aus sich'' <span style="color:#00B000">(von Natur aus)</span> ''vollkommen''«</span> ist ''':''' <span style="color:#4C58FF">— ‚'''□PG'''‘ —</span>, <span style="color:#00B000">(eine Instanz von Axiom-4)</span>. <span style="color:#FF6000">„Vollkommenheit“</span>, <span style="color:#4C58FF">— ‚'''P'''‘ —</span>, ist die <span style="color:#FF6000">„Wesenseigenschaft“</span>, bzw. das <span style="color:#FF6000">„Wesen GOTTES“</span> ''':''' <span style="color:#4C58FF"> — ‚'''G'''<sub>ess</sub>‘ —</span>. <span style="color:#FF6000">»''GOTT ist ,der‘ Vollkommenste''«</span> ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ —</span>, d.h. <span style="color:#4C58FF">— ‚'''G'''‘ —</span> ''':''' <span style="color:#FF6000">»''GOTT ist die unbegrenzte Summe der unendlichen Vollkommenheiten''«</span>. Und zur absoluten <span style="color:#FF6000">„Vollkommenheit“</span> gehört <span style="color:#FF6000">„notwendig“</span> auch das <span style="color:#FF6000">„Existieren“</span> ''':''' <span style="color:#4C58FF">— ‚'''PE'''<sub>not</sub>‘ — </span>. <span style="color:#FF6000">»''Notwendige Existenz''«</span> gehört zu den ,ultimativen‘ Transzendentalia in GOTT, was GÖDEL mit Definition-3 ''':''' <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>'''x ↔ ∀X(X'''<sub>ess</sub>'''x →□∃yXy)'''‘ —</span> syntaktisch formalisiert hat, wenn sowohl das <span style="color:#4C58FF">— ‚'''x'''‘ —</span>, als dann auch alle <span style="color:#4C58FF">— ‚'''∀y'''‘ —</span> im Kalkül, für den Dreifaltigen GOTT der Christen stehen, was im FUHRMANN-Korollar-3 ''':''' <span style="color:#4C58FF">— ‚'''□(∃xGx ∧ ∀y(Gy→x=y))'''‘ —</span>, mit der syntaktischen Identität, <span style="color:#4C58FF">— ‚'''x=y'''‘ —</span>, bzw. der ,Koinzidenz‘ beider Individuum-Variablen, explizit gezeigt wird.
Entscheidend für diese Interpretation des GÖDEL-Systems ist ''':''' nur unter der ,modal‘ notwendigen Voraussetzung der ontologischen ‚Identität‘ von <span style="color:#FF6000">„Sein“</span> und <span style="color:#FF6000">„Wesen“</span> in GOTT, Theorem-2 ''':''' <span style="color:#4C58FF"> — '''Gx↔G'''<sub>ess</sub>'''x'''‘ —</span>, bzw. der ‚Gleichsetzung‘, <span style="color:#00B000">(Koinzidenz)</span>, von <span style="color:#FF6000">„notwendiger Existenz“</span> mit den ‚positiven‘ Wesenseigenschaften, der <span style="color:#FF6000">„Essenz“</span> in GOTT, Axiom-5 ''':''' <span style="color:#4C58FF">— ‚'''PE'''<sub>not</sub>‘ —</span>, ‚funktioniert‘ die GÖDEL-Axiomatik '''!''' Diese ‚Identität‘, bzw. ,Koinzidenz‘ wird in ARISTOTELES, ‚''<span style="font-family: Times;"><big>Metaphysik</big></span>''‘, Buch XII 7, in einem Indizienbeweis erbracht, der mit der Methode der philosophischen Induktion zum Ergebnis kommt ''':'''
::<span style="color:#FF6000">» … ''es muss'' <span style="color:#00B000">[ notwendig ]</span> ''etwas geben, das, ohne selbst ‚bewegt‘'' <span style="color:#00B000">[ </span>''worden''<span style="color:#00B000"> ]</span> ''zu sein'', <span style="color:#00B000">[ ‚unentstanden‘ ]</span>, ''alles Übrige wie ein Geliebtes ‚bewegt‘'' <span style="color:#00B000">[ ‚entstehen lässt‘ ]</span>«</span>, das darum selbst ‚zugleich‘ <span style="color:#FF6000">„<span style="font-family: Times;"><big>αἴδιον καί οὐσία καί ἐνέργεια οὖσα</big></span>“ <span style="color:#00B000">| ,aídion kai ousía kai enérgeia oúsa‘ |</span> »<span style="color:#00B000">[ zeitlich-]</span>''ewig, sowohl <u>Wesen</u>'', <span style="color:#00B000">[ etwas Konkretes, Essentielles ]</span>, ''als auch seiende Wirksamkeit — ''<span style="color:#00B000">[ </span>„<span style="font-family: Times;"><big>actus purus</big></span>“, „reine Tätigkeit“<span style="color:#00B000"> ]</span> ''— verwirklichendes, wirkliches <u>Sein</u> ist'', <span style="color:#00B000">[ ein Existierendes, das alles Übrige ,zur Existenz‘ bringen kann ]</span> «</span>, bzw. <span style="color:#FF6000">„<span style="font-family: Times;"><big>ὀρεκτόν καί νοητόν</big></span>“ <span style="color:#00B000"> | ,orektón kai noêtón‘ | </span>»''das ersehnt und erkennbar ist''.«</span> <span style="color:#00B000">(''<span style="font-family: Times;"><big>vgl. ,Metaphysik</big></span>''‘ XII 7, 1072a,23 – 1072b,4)</span>
Was <span style="color:#FF6000">»''alles Übrige''«</span> ,zur Existenz‘ bringen kann, bzw. ,verwirklichen‘ kann, ohne selbst ,entstanden‘ zu sein, muß auch selbst, als etwas Konkretes, Essentielles, immer schon ,existieren‘, bzw. ,wirklich sein‘. Die, daraus abgeleitete, und damit ,erkannte‘, ontologische ‚Identität‘, — ,Koinzidenz‘ —, von ‚Wesen‘ und ‚Sein‘, <span style="color:#00B000">(Ziel aller Sehnsucht und jedes Erkenntnisstrebens)</span>, <span style="color:#FF6000">»''ist das Privilegium der Gottheit allein''«</span> ''':''' mit Gottfried Wilhelm LEIBNIZ interpretiert, entsprechend einer adäquaten, aristotelisch-<span style="color:#4C58FF">,theologischen‘</span> Tradition. Dieses induktive, ‚ontologisch‘ a-posteriori Ergebnis aus der ‚Prinzipienforschung‘ des ARISTOTELES ist die metaphysische und logische Voraussetzung, dass GÖDEL seine Axiomatik im Kalkül des sog. ‚ontologischen Gottesbeweises‘ a-priori des ANSELM von Canterbury, und nach LEIBNIZ, deduktiv korrekt formulieren konnte; <span style="color:#00B000">(vgl. 3. Beweisgang)</span>.
Angenommen, die Variable <span style="color:#4C58FF">— ‚'''x'''‘ —</span> steht für den <span style="color:#FF6000">„GOTT“</span>, <span style="color:#4C58FF">— ‚'''G'''‘ —</span>, der Christen, <span style="color:#00B000">(siehe Anhang, Term :01: im 2. Beweisgang)</span>, dann ist, — auf Grund von diesem Beweisgang —, in unserer Welt ,wahr‘ und evident ''':''' sowohl die ‚positive Eigenschaft‘ <span style="color:#FF6000">»''Göttlichkeit''«</span>, <span style="color:#4C58FF">— ‚'''G'''‘ —</span>, als auch das faktische <span style="color:#FF6000">»''‚Da‘-Sein'' <span style="color:#00B000">[ Existenz ]</span> ''GOTTES''«</span>, <span style="color:#4C58FF">— ‚'''G'''‘ —</span>, ‚benennen‘, ontologisch ident, denselben Sachverhalt ''':''' nämlich das, was wir das <span style="color:#FF6000">»''Wesen'' <span style="color:#00B000">[ Essenz ]</span> ''GOTTES''«</span>, <span style="color:#4C58FF"> — ‚'''G'''<sub>ess</sub>‘ —</span>, nennen. <span style="color:#FF6000">»''Göttlichkeit'', bzw. ''GOTT-‚Sein‘ ist das Wesen GOTTES''«</span>, <span style="color:#00B000">(Theorem-2.1)</span>, und dann umgedreht und äquivalent ''':''' <span style="color:#FF6000">»''Das Wesen GOTTES ist sein ‚Da‘-Sein als GOTT'', bzw. ''seine Göttlichkeit''«</span>, <span style="color:#00B000">(Theorem-2.2)</span>, m.a.W. ''':''' <span style="color:#FF6000">»''GOTT ist wesentlich ‚grundlos‘'' <span style="color:#00B000">[ d.h. </span> ''notwendig aus sich''<span style="color:#00B000"> ]</span> ''‚da‘''«</span>. Das ist das Einzigartige im <span style="color:#FF6000">»''Wesen GOTTES''«</span> ''':''' GOTT ist ohne zeitlichen Anfang, zeitlos-ewig, für uns immer schon ‚da‘, und das ‚ist‘ sein <span style="color:#FF6000">»''Wesen''«</span>; vorausgesetzt, ,angenommen‘, man glaubt an GOTT ''':''' Term :01:. <span style="color:#00B000">(Der schon von GÖDEL indizierte Term <span style="color:#4C58FF"> — ‚'''G'''<sub>ess</sub>‘ — </span> ,expliziert‘ nur eine der drei Lesearten, die der Term <span style="color:#4C58FF"> — ‚'''G'''‘ — „theologisch“</span> ,impliziert‘.)</span> Theorem-2 hat somit die syntaktische Form einer Definition ''':'''
<div class="center"><span style="color:#4C58FF"> — ‚'''Gx↔G'''<sub>ess</sub>'''x'''‘ — </span></div>
Somit kann GOTT ‚explizit‘ <span style="color:#00B000">(aus einer bewiesenen Kalkül-Definition)</span> ontologisch-<span style="color:#4C58FF">„theologisch“</span> genauer ‚bestimmt‘ werden ''':''' <span style="color:#FF6000">»''GOTT ist gerade deswegen GOTT, weil sein überzeitlich-ewiges und an sich ‚grundloses‘'' <span style="color:#00B000">[ aber für uns notwendiges ]</span> ''Dasein'' <span style="color:#00B000">[ Existenz ]</span> ''als GOTT, ontologisch, — dem Sein nach —, identisch ist mit seinem persönlichen und für uns liebevollen Wesen'' <span style="color:#00B000">[ Essenz ]</span> ''als GOTT; diese Identität von Dasein und Wesen gilt einzig und allein nur bei GOTT.''«</span> Damit hat GÖDEL ein altes <span style="color:#4C58FF">„theologisches“</span> Theorem, philosophisch der Vergessenheit entrissen. Die philosophische Frage nach dem <span style="color:#FF6000">„Wesen GOTTES“</span> lautet, <span style="color:#00B000">(auf die Person bezogen)</span> ''':''' <span style="color:#FF6000">„Was bist du ? “</span> Sie ist äquivalent zur <span style="color:#4C58FF">,theologisch’</span>-biblischen Frage des MOSE ''':''' <span style="color:#CC66FF">„Wer bist Du ? “</span> Die bekannte Antwort des GOTTES-JHWH aus ‚Exodus 3,14‘, im Dornbusch, thematisiert das persönliche, für uns liebevolle und für immer notwendige <span style="color:#FF6000">„Dasein GOTTES“</span> ''':''' <span style="font-family: Times;"><big>‘אֶֽהְיֶ֖ה אֲשֶׁ֣ר אֶֽהְיֶ֑ה‚</big></span> <span style="color:#00B000">| ‚eh'jeh asher eh'jeh‘ |</span> <span style="color:#CC66FF">»''Ich bin der ‚Ich-Bin-Da‘'', <span style="color:#00B000">[ für euch und für immer ]</span>«.</span> Mit diesem Zitat aus der Bibel ist die GÖDEL-Axiomatik, sozusagen, <span style="color:#4C58FF">„theologisch“</span> ‚verifiziert‘. Sie hat einerseits im Theorem-2 ihren philosophischen ‚Abschluss’ erreicht, und andererseits damit formal-syntaktisch den ‚Anschluss‘ an eine allgemeine Basis-Glaubensaussage gefunden, die ‚an sich‘ für jeden CHRIST-gläubigen Menschen ‚selbstverständlich‘ ist. Was in der Metaphysik des ARISTOTELES das Ergebnis einer philosophischen ,Induktion‘ a-posteriori ist ''':''' <span style="color:#FF6000">„,Dasein‘ ist das ,Wesen‘ GOTTES“</span>, — <span style="color:#00B000">(das mit Theorem-2, auch ein Ergebnis der deduktiven GÖDEL-Axiomatik a-priori ist ''':''' die Beweisgrundlage für den Konsequenz-Teil im Theorem AMSELMS)</span>, — das ist in der Bibel die Grundüberzeugung jedes Menschen, der an GOTT glaubt ''':''' GOTT ist für uns immer schon <span style="color:#FF6000">„da“</span>, weil er uns liebt. Das ist das, <span style="color:#FF6000">„was“</span> GOTT für uns als GOTT ausmacht, — sein Wesen ''':''' <span style="color:#CC66FF">»''Wir haben die Liebe, die GOTT zu uns hat, erkannt und gläubig angenommen. GOTT ist Liebe, und wer in der Liebe bleibt, bleibt in GOTT und GOTT bleibt in ihm''«</span>, <small>({{Bibel | 1. Johannesbrief |4|16|EU}})</small>.
Das eigentliche Ergebnis der GÖDEL-Axiomatik ist somit die ‚triviale‘ Erkenntnis, dass GOTT, <span style="color:#FF6000">„unverursacht“ <span style="color:#00B000">|</span> „grundlos“</span>, für uns immer schon ‚da‘ ist, — <span style="color:#CC66FF">„von Ewigkeit zu Ewigkeit“</span> —, vorausgesetzt <span style="color:#00B000">(‚angenommen‘)</span>, man ‚glaubt‘ an den zeitlos-ewigen GOTT ''':''' <span style="color:#CC66FF">»''Ehe die Berge geboren wurden, ehe die Erde entstand und das Weltall erschaffen wurde, bist du, o GOTT, von Ewigkeit zu Ewigkeit''«</span>, <small>({{Bibel | Psalm |90|2|LUT}})</small>. <span style="color:#00B000">(Der Glaube an die Zeitlosigkeit GOTTES ist mit der ‚Annahme‘ von Axiom-3 ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ —</span> ''':''' <span style="color:#FF6000">»''GOTT ist perfekt''«</span>, und der ‚Annahme‘ der Definition-1 für <span style="color:#4C58FF"> ‚'''Gx'''‘ := </span> den <span style="color:#FF6000">„GOTT der Christen“</span>, im Kalkül ‚implizit‘ schon eingeführt, da die Axiome und Definitionen, — nach GÖDEL —, nur dann <span style="color:#FF6000">»''wahr''«</span> sind, wenn sie <span style="color:#FF6000">»''unabhängig von der zufälligen''</span> [ Raum-Zeit-]<span style="color:#FF6000">''Struktur''«</span> unserer Welt sind. Das ,impliziert‘ auch, dass der GOTT von Axiom-3 und Definition-1 ebenfalls <span style="color:#FF6000">»''unabhängig''«</span> von Raum und Zeit, d.h. zeitlos-ewig ist '''!''' )</span>
Wer an den GOTT der Bibel glaubt, kann sich von der ‚Vernünftigkeit‘ seines Glaubens mit Hilfe des sog. ,ontologischen‘ Gottesbeweises nach ANSELM von Canterbury, mit Kurt GÖDEL <span style="color:#FF6000">»''rein verstandesmäßig''«</span>, überzeugen. <span style="color:#00B000">(Das war auch die Absicht ANSEMS '''!''' )</span> Die Annahme, es sei ‚unmöglich‘, dass es GOTT gibt ''':''' <span style="color:#4C58FF">— ‚'''¬◇∃xGx'''‘ —</span>, <span style="color:#00B000">(dezidierter Atheismus)</span>, führt im GÖDEL-Kalkül formal zu einem logischen Widerspruch; vgl. z. B. ‚''<span style="font-family: Times;"><big>Gödels Möglichkeitsbeweis</big></span>''‘, in ‚''<span style="font-family: Times;"><big>Der Gödelsche Gottesbeweis</big></span>''‘, Seite 17, von Günther J. WIRSCHING; (https://edoc.ku.de/id/eprint/10243/1/OntBw.pdf), d.h. es ist also nicht ‚unmöglich‘, dass es GOTT gibt. Der GOTT-Glaube ist mit den Maßstäben einer modernen Logik <span style="color:#FF6000">»''durchaus vereinbar''«</span> und darum ,vernünftig‘. Damit steht fest ''':''' das GÖDEL-Kalkül ist kein moderner ‚Existenz-Beweis‘ für den GOTT der Bibel, sondern es setzt, <span style="color:#FF6000">»''rein verstandesmäßig''«</span>, ,methodologisch‘, den Glauben an die Existenz eines zeitlos-ewigen GOTTES voraus, der, — <span style="color:#FF6000">»''unabhängig von der zufälligen'' <span style="color:#00B000">[ Raum-Zeit-]</span>''Struktur unserer '' <span style="color:#00B000">[ vergänglichen ]</span> ''Welt''«</span> —, für uns immer schon ‚da‘ ist. Wenn aber einmal als fix ‚angenommen‘ worden ist, <span style="color:#00B000">(als Prämisse)</span>, dass es wahr ist, dass GOTT ‚existiert‘, dann ist natürlich die ‚Annahme‘, dass GOTT ‚nicht existiert‘, falsch. Aber sie ist auch ,unlogisch‘ und ,unsinnig‘, weil die Annahme ''':''' <span style="color:#FF6000">»''Es ist unmöglich, dass es einen GOTT gibt''«</span>, offensichtlich und eindeutig zu einem Widerspruch führt; was z. B. Günther J. WIRSCHING mit seiner Version des <span style="color:#00B000">(nicht umkehrbaren)</span> ‚Möglichkeitsbeweises‘ für ,GOTT‘, explizit vorexerziert hat; <span style="color:#00B000">( nachgestellt im Anhang ''':''' GÖDELS ‚Möglichkeitsbeweis‘ als ,Widerlegung‘ eines Nicht-GOTT-Glaubens; in Entsprechung zu Psalm 14,1 und Psalm 53,2 ''':''' <span style="color:#CC66FF">»''Der'' <span style="color:#00B000">[ ,unvernünftige‘ ]</span> ''Tor sagt in seinem Herzen ''':''' Es gibt keinen Gott. Sie handeln verderbt, handeln abscheulich; da ist keiner, der Gutes tut''«</span>. Historischer Hintergrund zu diesem Psalm-Text ''':''' Die Zerstörung des Tempels in Jerusalem 586 v.Ch. durch die Truppen des NEBUKADNEZZAR II.)</span> Der Logiker GÖDEL hat in seinem System zum ,ontologischen Beweis‘ keine ‚formale Unentscheidbarkeit‘ <span style="color:#00B000">(Agnostizismus)</span> feststellen können, wie auf einem anderen Feld seiner Forschungsarbeiten.
Das GÖDEL-Konsequenz-Teil von der ‚Notwendigkeit‘ GOTTES ''':''' <span style="color:#4C58FF">— ‚'''□∃xGx'''‘ —</span>, <span style="color:#00B000">(die ‚Konsequenz’ aus dem ‚widerspruchsfreien‘ Korollar-1 ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx'''‘ —,</span>)</span> im ‚Theorem ANSELMS‘, ist <span style="color:#00B000">(im 3. Beweisgang, Term :10:)</span> dann auch eine weitere Explikation des Basis-Theorems-2 des Kalküls ''':''' <span style="color:#4C58FF"> — ‚'''Gx↔G'''<sub>ess</sub>'''x'''‘ — </span>, über die ‚ontologische Identität‘ vom <span style="color:#FF6000">„Dasein GOTTES“</span> ''':''' <span style="color:#4C58FF">— ‚'''G'''‘ —</span>, mit seinem <span style="color:#FF6000">„Wesen“</span> ''':''' <span style="color:#4C58FF"> — <sub>ess</sub> —</span>, nach einem <span style="color:#4C58FF">[ Modus ponens ]</span>, dargestellt mit Term :09:.
<span style="color:#00B000">(Die ontologische Identität von Dasein und Wesen in GOTT, ist die, für uns, <span style="color:#FF6000">„notwendige Präsenz <span style="color:#00B000">[ das Sein, <span style="color:#4C58FF">— ‚'''E'''‘ —</span>]</span> GOTTES“</span> ''':''' <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>'''x'''‘ —</span>, die äquivalent, bzw. koinzident ist zur <span style="color:#FF6000">„Vollkommenheit <span style="color:#00B000">[ das Wesen, <span style="color:#4C58FF">— ‚'''P'''‘ —</span>]</span> GOTTES“</span> ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ —</span>. Diese Identität von Sein und Wesen in GOTT ''':''' <span style="color:#4C58FF">— ‚'''PE'''<sub>not</sub> —</span>, bedeutet <span style="color:#4C58FF">,theologisch‘</span> konkret ''':''' die, für uns, notwendige Gegenwärtigkeit GOTTES, [ sein Dasein ], ist verwirklicht worden in der liebevollen [ Wesens-]Zuwendung GOTTES zu uns Menschen, in seiner Kindwerdung in Bethlehem, durch die Jungfrau MARIA ''':''' GOTTES Wesen ist ,Sein-mit-uns‘ ''':''' <span style="color:#CC66FF">»''sein Name'', <span style="color:#00B000">[ sein Wesen ]</span>, ''ist IMMANUEL, das heiß übersetzt ''':''' GOTT-ist-mit-uns'', <span style="color:#00B000">[ d.i. sein Dasein mit und für uns ]</span>«, <small>({{Bibel | Matthäus Evangelium |1|23|EU}})</small></span>, der unsere Not-,wenden‘-wird, d.h. der uns und die Welt von der Korruption der Sünde und des Todes <span style="color:#4C58FF">,erlösen‘</span> will und wird; jedoch nicht ohne unsere Zustimmung. Die <span style="color:#4C58FF">„Menschwerdung“</span> GOTTES in JESUS CHRISTUS, die <span style="color:#4C58FF">„Hypostatische Union“</span>, bzw. diese außergewöhnliche und einzigartige <span style="color:#FF6000">„Koinzidenz“</span> von GOTT und Mensch, ist der wirkliche und historische Beginn der <span style="color:#4C58FF">„Erlösung“</span> des Menschen und der Welt; vergegenwärtigt für jede Generation der Menschen in und durch die <span style="color:#4C58FF">„Kirche“</span> JESU CHRISTI. Damit bekommt jede Generation die Chance, die <span style="color:#4C58FF">„Erlösung“</span> durch JESUS CHRISTUS für sich ,annehmen‘ zu können.)</span>
Die, von GÖDEL im 1. Beweisgang, als Prämissen schon vorausgesetzten und ,angenommenen‘ Perfektionen, bzw. Vollkommenheiten, <span style="color:#00B000">(das sind die allgemeinen ,Transzendentalia‘ für alles Nicht-Göttliche in der Welt, durch Beispiele belegbar)</span>, werden im ersten Teil des 2. Beweisganges, mit Term :13: <span style="color:#4C58FF">— ‚'''PY'''‘ —</span>, dann auch als <span style="color:#FF6000">„positive Wesenseigenschaften“</span>, <span style="color:#00B000">(als die ultimativen ,Transzendentalia‘)</span>, in GOTT ‚definitiv‘ <span style="color:#00B000">( ╞ )</span> bestätigt; <span style="color:#00B000">(siehe Anhang, 2. Beweisgang, Anmerkung-2)</span>.
Im 3. Beweisgang ist das Basis-Theorem-2 die ,modal‘ notwendige, bzw. transzendentale, Voraussetzung, sowohl für das <span style="color:#FF6000">„an sich notwendige Dasein GOTTES“</span>, <span style="color:#4C58FF">— ‚'''□∃xGx'''‘ —</span>, im Term :10:, als auch für die <span style="color:#FF6000">„für uns notwendige Existenz GOTTES“</span>, <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>'''x'''‘ —</span>, in den Ressourcen dieses Beweisganges ''':''' in der Definition-3, und im Axiom-5 für <span style="color:#4C58FF">— ‚'''PE'''<sub>not</sub>‘ —</span>; und das bedeutet auch eine Äquivalenz beider Terme ''':''' <span style="color:#4C58FF">— ‚'''□∃xGx ↔ E'''<sub>not</sub>'''x'''‘ —</span>, bzw. mit <span style="color:#4C58FF">— ‚'''PE'''<sub>not</sub>‘ —</span>; <span style="color:#00B000">(das <span style="color:#4C58FF">— ‚'''E'''‘ —</span> wird nur GOTT zugeordnet, vgl. auch Anhang, 3. Beweisgang, Anmerkung-4)</span>. Dieses Basis-Theorem-2 ist auch zugleich die Antwort auf die Frage nach dem ‚Ursprung‘ GOTTES ''':''' GOTT ist <span style="color:#FF6000">„unverursacht“ <span style="color:#00B000">|</span> „ursprungslos“</span> ‚da‘, von <span style="color:#CC66FF">„Ewigkeit zu Ewigkeit“</span>, denn es ‚ist‘ sein <span style="color:#FF6000">„Wesen“</span>, <span style="color:#00B000">(überzeitlich-ewig)</span> für uns immer schon ‚da‘ zu sein. Weitere ‚Einzelheiten‘ über Wesen und Eigenschaften GOTTES gehören in die Mystik, bzw. in die <span style="color:#4C58FF">„Theologie“</span>.
===<div class="center"><span style="color:#660066">Die Bedeutung des Kalküls</span></div>===
<div class="center">Immanuel KANT und Kurt GÖDEL im ‚Dialog‘</div>
KANT sagt ''':'''
::<span style="color:#FF6000">»</span>'''''Sein''' ist offenbar kein reales Prädikat''. ... ''Es ist bloß die'' <span style="color:#00B000">[ gedachte ]</span>'' Position'' <span style="color:#00B000">[ latinisiert, deutsch für ''':''' ,Setzung‘ ]</span> ''eines Dinges ... Nehme ich nun das Subjekt (Gott) mit allen seinen Prädikaten'' <span style="color:#00B000">[ d.h. Eigenschaften ]</span> ''(worunter auch die Allmacht gehört) zusammen, und sage ''':''' ‚'''Gott ist'''‘'', <span style="color:#00B000">[ ,GOTT existiert wirklich‘ ]</span>, ''oder ‚es ist ein Gott‘, so <u>setze</u> ich kein neues Prädikat'' <span style="color:#00B000">[ keine neue Eigenschaft ]</span> ''zum ‚Begriffe‘ von Gott ''':''''' <span style="color:#00B000">[ <span style="color:#0000FF; background-color:#FFFF00">‚Sein’ ist kein ‚reales Prädikat’ in GOTT</span>; ‚Existenz‘ ist in GOTT keine ‚Eigenschaft‘ ],</span> ... ''es kann daher zu dem Begriffe'', <span style="color:#00B000">[ ,GOTT‘ ]</span>, ''der bloß die'' <span style="color:#00B000">[ gedachte ]</span>'' Möglichkeit ausdrückt, darum, dass ich dessen Gegenstand'', <span style="color:#00B000">[ GOTT ]</span>, ''als schlechthin gegeben (durch den Ausdruck ''':''' er ist'', <span style="color:#00B000">[ GOTT ist wirklich ]</span>'' ) <u>denke</u>, nichts weiter hinzukommen.'' <span style="color:#00B000">[ Beides ist ,bloß gedacht‘ '''!''' ]</span> ''Und so enthält das Wirkliche <u>nichts mehr</u> als das bloß Mögliche. Hundert ‚wirkliche‘ Taler enthalten nicht das mindeste <u>mehr</u>, als hundert ‚mögliche‘. Denn, da diese'', <span style="color:#00B000">[ die ,Möglichen‘ ]</span>, ''den'' <span style="color:#00B000">[ gedachten ]</span>'' ‚Begriff‘, jene'', <span style="color:#00B000">[ die ,Wirklichen‘ ]</span>, ''aber den Gegenstand und dessen'' <span style="color:#00B000">[ gedachte ]</span>'' Position an sich selbst bedeuten, so würde, im Fall dieser'', <span style="color:#00B000">[ die 100, als ,wirklich‘ bloß gedachten Taler ]</span>, ''<u>mehr</u> enthielte als jener,'' <span style="color:#00B000">[ als ihr ‚gedachter‘ Begriff im Verstand, wie ΑNSELM von Canterbury für GOTT, als ‚wirklich‘ Existierenden, argumentierte, …''so würde'' ]</span> ''mein ‚Begriff‘'' <span style="color:#00B000">[ die 100 im Verstand ‚gedachten‘ Taler ]</span> ''nicht den ganzen Gegenstand ausdrücken, und also auch <u>nicht der angemessene Begriff</u> von ihm sein. Aber in meinem Vermögenszustande ist <u>mehr</u> bei hundert ‚wirklichen‘ Talern, als bei dem bloßen Begriffe derselben'', <span style="color:#00B000">[ als bei 100 bloß ‚gedachten‘ Talern ]</span> ... <span style="color:#FF6000">«</span> <span style="color:#00B000"><ref>‚''<span style="font-family: Times;"><big>Kritik der reinen Vernunft</big></span>''‘, Seite 401; https://www.korpora.org/kant/aa03/401.html</ref></span>.
GÖDEL würde darauf <span style="color:#00B000">(korrespondierend zur aristotelisch-<span style="color:#4C58FF">,theologischen‘</span> Tradition von der Identität von Sein und Wesen in GOTT)</span> antworten ''':'''
::<span style="color:#FF6000">»</span>Die <span style="color:#FF6000">„100 Taler“</span> sind der <span style="color:#FF6000">»''zufälligen Struktur der'' <span style="color:#00B000">[ vergänglichen ]</span> ''Welt''«</span> entnommen, und sind daher nicht mit GOTT vergleichbar, der, <span style="color:#FF6000">»''unabhängig von der zufälligen'' <span style="color:#00B000">[ Raum-Zeit-]</span>''Struktur''«</span> unserer Welt, <span style="color:#FF6000">„über“</span> dieser Welt steht. Einzig und allein nur von GOTT gilt ''':''' Der mit Dingen aus unserer Welt ,nicht vergleichbare‘ GOTT, <span style="color:#4C58FF">— ‚'''Gx'''‘ —</span>, <span style="color:#FF6000">„existiert notwendig für uns“</span>, <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>'''x'''‘ —</span>, und <span style="color:#FF6000">„notwendiges Existieren, <u>Sein</u>“</span> ,ist‘ die <span style="color:#FF6000">„positive <u>Wesen</u>seigenschaft“</span> in GOTT ''':''' <span style="color:#4C58FF">— ‚'''PE'''<sub>not</sub>‘ —</span>, weil GOTT aus sich <span style="color:#FF6000">„vollkommen“ <span style="color:#00B000">|</span> „perfekt“</span> ist, <span style="color:#4C58FF">— ‚'''PG'''‘ —</span> ''':''' <span style="color:#0000FF; background-color:#FFFF00">‚Sein‘ ist in GOTT ein ‚reales Prädikat‘</span>; <span style="color:#00B000">(notwendige ‚Existenz’ ist eine positive ‚Wesenseigenschaft’ in GOTT)</span>, und nur bei GOTT '''!''' Zum zeitlos-ewigen GOTT der Christen ''':''' <span style="color:#4C58FF">— ‚'''Gx'''‘ —</span>, <span style="color:#00B000">(als methodologische Kalkül-Prämisse)</span>, kann man sagen ''':''' Weil es, wegen Axiom-1 und Axiom-2, <span style="color:#FF6000">„widerspruchsfrei möglich"</span> ist, dass es GOTT gibt ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx'''‘ —</span>, darum ist GOTT auch das ‚einzige‘ <span style="color:#FF6000">„Wesen“</span>, <span style="color:#4C58FF">— ‚'''∃x'''‘ —</span>, von dem ein Christ sagen kann, dass es sowohl <span style="color:#FF6000">„notwendig aus sich“</span> ''':''' <span style="color:#4C58FF">— ‚'''□∃xGx'''‘ —</span>, d.h. <span style="color:#FF6000">„grundlos“ <span style="color:#00B000">|</span> „unverursacht“</span>, als auch <span style="color:#FF6000">„notwendig für uns“</span> ''':''' <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>'''x'''‘ —</span>, immer schon ‚da’ war und immer ,da’ sein wird; und zusätzlich gilt ''':''' Es gibt für jede mögliche Welt ‚nur‘ diesen einen GOTT ''':''' <span style="color:#4C58FF">— ‚'''□∃xGx ∧ □∀y(Gy→x=y)'''‘ — </span><span style="color:#00B000">(Monotheïsmus, Korollar-3)</span>; vorausgesetzt, man geht von der ,Existenz’ dieses GOTTES aus, wobei diese Annahme für GÖDEL <span style="color:#FF6000">»''mit allen bekannten Tatsachen durchaus vereinbar''«</span> ist.<span style="color:#FF6000">«</span>
Eine Beobachtung ''':''' KANT sagt, gleichsam als ,krönenden‘ Abschluss seiner Widerlegung des, — von ihm so genannten —, ,ontologischen Gottesbeweises‘ ''':''' <span style="color:#FF6000">»</span>''Aber in meinem Vermögenszustande ist <u>mehr</u> bei hundert ‚wirklichen‘ Talern, als bei dem bloßen Begriffe derselben, (d.i. ihrer Möglichkeit).''<span style="color:#FF6000">«</span> Diese Feststellung KANTS entspricht jedoch genau der Argumentation ANSELMS ''':''' GOTT <span style="color:#FF6000">„<span style="font-family: Times;"><big>esse et in re</big></span>“</span>, d.h. GOTT ,existiert auch in Wirklichkeit‘, <span style="color:#FF6000">„<span style="font-family: Times;"><big>quod maius est</big></span>“</span>, was <u>mehr</u> ist, als <span style="color:#FF6000">„<span style="font-family: Times;"><big>esse solo in intellectu</big></span>“</span>, als nur ein bloßer Begriff ,im Verstand zu sein‘. Der ,Mehr-Wert‘ ergibt sich in beiden Fällen, sowohl bei den Talern als auch bei GOTT, aus der ,Wirklichkeit‘ ihrer Existenz, im Gegensatz zur bloßen, <span style="color:#00B000">(im Begriff gedachten)</span>, ,Möglichkeit‘ ihrer Existenz, so dass, in jedem Fall, der ,Begriff‘ im Verstand ohne Abstriche <span style="color:#FF6000">»</span>''den ganzen Gegenstand ausdrückt''<span style="color:#FF6000">«</span>, und von diesem auch <span style="color:#FF6000">»</span>''der angemessene Begriff''<span style="color:#FF6000">«</span> ist. Alles andere wäre eine ,Lüge‘. Mit dieser ,Beobachtung‘ ist das implizit ,Widersprüchliche‘ in KANTS Argumentation aufgedeckt ''':''' Das Wirkliche in KANTS Vermögenszustande enthält <span style="color:#FF6000">,doch mehr‘</span> als das bloß Mögliche, konträr zu seiner vorigen Behauptung ''':''' <span style="color:#FF6000">»</span>''das Wirkliche''<span style="color:#FF6000">«</span> enthalte <span style="color:#FF6000">»'',nichts mehr‘</span> als das bloß Mögliche''<span style="color:#FF6000">«</span>. Diese Behauptung ist offensichtlich falsch. Das ist somit ein indirekter Beweis und damit eine Bestätigung für die analoge Argumentation ANSELMS aus dem Widerspruch des Gegenteils, am Beispiel KANTS <span style="color:#FF6000">»</span>''Vermögenzustandes bei hundert wirklichen Talern''<span style="color:#FF6000">«</span>, in dem in Wirklichkeit <span style="color:#FF6000">,doch mehr‘</span> ist, <span style="color:#FF6000">»</span>''als bei dem bloßen Begriffe derselben''<span style="color:#FF6000">«</span>.
<span style="color:#00B000">(Diese ,Beobachtung‘ ist zugleich auch das entscheidende Indiz dafür, dass das systembedingte Konzept KANTS von der ,Existenz‘, bzw. vom ,Sein‘ eines jeden Gegenstandes, als</span> <span style="color:#FF6000">»</span>''dessen bloße Position''<span style="color:#FF6000">«</span>, <span style="color:#00B000">d.i. als seine ,Setzung‘ bloß im- und durch den Verstand, ,falsch‘ ist, — d.h. im Klartext ''':''' für KANT ist das ,Sein‘ eines Gegenstandes bloß ein ,Gedanke‘ in uns, wenn er meint, dass uns ein Gegenstand erst dann wirklich ,gegeben‘ sei, wenn wir uns den</span> <span style="color:#FF6000">»</span>''Gegenstand als schlechthin gegeben (durch den Ausdruck : <u>er ist</u>) <u>denken</u>''<span style="color:#FF6000">«</span>, <span style="color:#00B000">was nur dem Irrtum einer falschen System-Konzeption geschuldet sein kann. Auf Grund dieser Konzeption ist das</span> <span style="color:#FF6000">»</span>''Ding, wie es an sich selbst ist''<span style="color:#FF6000">«</span>, <span style="color:#00B000">für KANT systembedingt weder ,anschaubar‘, noch ,erkennbar‘. Diese falsche Konzeption über die ,Existenz‘, bzw. das ,Sein‘ eines Dinges, als</span> <span style="color:#FF6000">»</span>''dessen bloße Position''<span style="color:#FF6000">«</span>, <span style="color:#00B000">ist für KANT letztendlich auch die Beweisgrundlage und Voraussetzung für seine Ablehnung des ontologischen Argumentes für GOTT im Theorem ANSELMS. Wenn das ,wirkliche‘ Sein eines Dinges nichts anderes ist, als</span> <span style="color:#FF6000">»</span>''dessen'' <span style="color:#00B000">[ bloß gedachte ]</span> ''Position''<span style="color:#FF6000">«</span>, <span style="color:#00B000">d.h. als seine ,mögliche Setzung‘ bloß im- und durch den Verstand, — das ist das, als ,wirklich‘ bloß nur ,gedachte‘ Ding —, dann</span> <span style="color:#FF6000">»</span>''enthält''<span style="color:#FF6000">«</span> <span style="color:#00B000">natürlich</span> <span style="color:#FF6000">»</span>''das Wirkliche''<span style="color:#00B000">, [ als die bloß gedachte Existenz ],</span> ''nichts mehr als das bloß Mögliche''<span style="color:#00B000">, [ als der gedachte Begriff ]<span style="color:#FF6000">«</span>, was offensichtlich unhaltbar ist. <span style="color:#4C58FF">[ Modus tollendo tollens ] </span> ''':''' Wenn die Konsequenz einer Wenn-Dann-Folgerung ,falsch‘ ist, dann ist auch ihre Voraussetzung, das System-Konzept KANTS, ,falsch‘ ''':''' d.i. seine ,Kopernikanische Wende‘ für die Metaphysik, soweit sie KANTS ,Sein’-Konzept betrifft. Korrekt und ,wahr‘ ist in jedem Fall ''':''' Das Wirkliche enthält <span style="color:#FF6000">,doch mehr‘</span> als das bloß Mögliche, und die Dinge ,existieren‘ schon immer unabhängig von unserem Denken. ,Existenz‘, das ,Sein‘, ist <span style="color:#FF6000">,doch mehr‘</span>, als bloß ein ,Gedanke‘ von uns.)</span>
Somit ist die Argumentation KANTS gegen den ontologischen Beweis ANSELMS für GOTT ,falsch‘ und unhaltbar, weil sie auf der ,falschen‘ Voraussetzung beruht ''':''' die ,Existenz‘, bzw. das ,Sein‘ eines jeden ,Gegenstandes‘, — wie z. B. auch die Existenz bei GOTT —, sei bloß dessen gedachte ,Position‘ an sich selbst, d.h. bloß seine ,Setzung‘ im- und durch den Verstand. Damit ,macht‘ er GOTT außerdem zu einem ,Ding‘ unter den vielen ,Dingen‘ dieser Welt, und verkennt so, — wie vor ihm THOMAS von Aquin —, auch die Einzigartigkeit und Exklusivität GOTTES im Theorem ANSELMS.
<div class="center">Die <span style="color:#FF6000">„Rede von GOTT“</span> in der philosophischen Tradition</div>
Wenn man die philosophische Tradition der <span style="color:#FF6000">„Rede von GOTT“</span> im Lichte der Ergebnisse der <span style="color:#4C58FF">„axiomatischen Theologie“</span> GÖDELS liest, dann stellt sie sich am Beispiel bei ARISTOTELES, — AVICENNA, — ANSELM, und bei GÖDEL wie folgt dar ''':'''
<span style="color:#FF6000">»''Das Erstbewegende,'' (<span style="font-family: Times;"><big>,πρῶτον κινοῦν‘</big></span>), ''das, ohne selbst ‚bewegt‘ zu sein'', (<span style="font-family: Times;"><big>,ἀκίνητον‘</big></span> <span style="color:#00B000">| ''unverursacht, ,entstehungslos‘'' |</span> ), ''alles Übrige wie ein Geliebtes ‚bewegt‘'', (<span style="font-family: Times;"><big>,κινεῖ δὴ ὡς ἐρώμενον‘</big></span> <span style="color:#00B000"> | ''-verursacht, ,entstehen‘ lässt'' |</span> ), ''ist sowohl'' <span style="color:#00B000">[ zeitlich-]</span>''ewiges ‚Wesen‘'', (<span style="font-family: Times;"><big>,ἀΐδιον καί οὐσία‘</big></span> <span style="color:#00B000">| ''‚Substanz‘'' |</span> ), ''als auch'' <span style="color:#00B000">[ zeitlich-]</span>''ewiges ‚wirksames, verwirklichendes Sein‘'', (<span style="font-family: Times;"><big>‚ἀΐδιον καί ἐνέργεια οὖσα‘ = ‚actus purus‘</big></span><span style="color:#00B000"> | '',reine Tätigkeit‘'' |</span> ), … ''ersehnt'', (<span style="font-family: Times;"><big>,ὀρεκτόν‘</big></span>), ''und erkennbar'', (<span style="font-family: Times;"><big>,νοητόν‘</big></span>), ... ''denn dies ist der ‚Gott‘'', (<span style="font-family: Times;"><big>,τοῦτο γὰρ ὁ θεός‘</big></span>), <span style="color:#4C58FF">— ‚'''Gx'''‘ —</span>, ''der'' <span style="color:#00B000">[ zeitlich-]</span>''Ewige'', (<span style="font-family: Times;"><big>,ἀΐδιον‘</big></span>), — ''der Unvergleichliche'', (<span style="font-family: Times;"><big>,ἄριστον‘</big></span> <span style="color:#00B000">| ''‚der Beste‘'' |</span> ), — ''der Lebendige'', (<span style="font-family: Times;"><big>,ζῷον‘</big></span> <span style="color:#00B000">| ,''das Leben selbst‘'' |</span> ), — ... ''so sagen wir ja'', (<span style="font-family: Times;"><big>,φαμὲν δὴ‘</big></span>), — ...«</span> ''':''' <span style="color:#00B000">(ARISTOTELES — Grieche)</span>.
Der ‚Begriff’ <span style="color:#FF6000">„GOTT“</span>, <span style="color:#4C58FF">— ‚'''G'''‘ —</span>, als ,Individuumname‘, ist synonym mit <span style="color:#FF6000">„göttliches ‚Da-Sein’“</span>, das sowohl <span style="color:#FF6000">„aus sich vollkommen“</span>, als auch <span style="color:#FF6000">„notwendig für uns“</span> ‚da‘ ist; <span style="color:#00B000">(das ist das, an sich, vollkommene ‚Was-Sein‘ GOTTES ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ — </span>, das zugleich, für uns, das notwendige ‚Da-Sein‘ GOTTES ''':''' <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>'''x'''‘</span> — ist)</span>; <span style="color:#CC66FF">„von Ewigkeit zu Ewigkeit“</span>. Das ist der <u>angemessene Begriff</u> von GOTT, und gilt ‚nur‘ von GOTT. Weil GOTT <span style="color:#FF6000">„vollkommen“</span> ist, ist <span style="color:#FF6000">„Da-Sein“ <span style="color:#00B000">|</span> „GOTT-Sein“ <span style="color:#00B000">|</span> „Göttlichkeit“</span> das <span style="color:#FF6000">„Wesen“</span> GOTTES ''':''' <span style="color:#4C58FF">— ‚'''Gx↔G'''<sub>ess</sub>'''x'''‘ —</span>. Im Unendlichen, GOTT, sind <span style="color:#FF6000">„Essenz“</span> und <span style="color:#FF6000">„Existenz“</span> koinzident ,eins‘, und daher untrennbar, und <span style="color:#FF6000">»''darum ist GOTT das einzige ‚Sein’, das notwendig an sich ‚da‘ ist''«</span> ''':''' <span style="color:#00B000">(ABU ALI SINA alias AVICENNA — Muslim)</span>.
Der <span style="color:#00B000">(gedachte)</span> ‚Eigenschafts-Begriff‘ <span style="color:#FF6000">„Vollkommenheit (die Größe) GOTTES“</span>, <span style="color:#4C58FF">— ‚'''PG'''‘ —</span>, <span style="color:#00B000">(‚Perfektion‘, die unbegrenzte Summe aller ‚positiven Eigenschaften‘ in GOTT)</span> schließt koinzident die ‚Eigenschaft’ <span style="color:#FF6000">„notwendige Existenz für uns“</span> mit ein ''':''' <span style="color:#4C58FF">— ‚'''PE'''<sub>not</sub>‘ —</span>. GOTT wäre nicht <span style="color:#FF6000">„vollkommen“</span>, wenn er nicht auch real für uns ‚da‘ wäre, wenn er nicht ,immer schon’ <span style="color:#FF6000">„existierte“</span>. ‚Sein’ ist <u>mehr</u> als ‚Nicht-Sein’. ,Sein’, bzw. ,Existenz’ gehört in GOTT zu den ,Transzendentalia’. Das sind die <span style="color:#00B000">(ultimativen)</span> ,Wesenseigenschaften’ in GOTT. Der unbegrenzt Unendliche, GOTT, ist daher das <span style="color:#FF6000">»''vollkommenste Wesen, über das nichts ,Größeres‘ d.h. Vollkommeneres <u>mehr</u> ‚gedacht‘ werden kann''«</span> ''':''' <span style="color:#00B000">(ANSELM von Canterbury — Christ)</span>.
Der ‚Begriff’ <span style="color:#FF6000">„Perfektion GOTTES“</span>, <span style="color:#4C58FF">— ‚'''PG'''‘ —</span>, schließt koinzident das <span style="color:#FF6000">„notwendige Dasein GOTTES“</span>, <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>'''x'''‘</span> —, für uns mit ein, ohne einen zeitlichen Anfang und ohne ein zeitliches Ende. Das ist die ‚zeitlos-ewige‘, an sich absolute, und <span style="color:#FF6000">„für uns notwendige Existenz GOTTES“</span> ''':''' <span style="color:#4C58FF">— ‚'''Gx→E'''<sub>not</sub>'''x'''‘ —</span> ''':''' <span style="color:#00B000">(Das ist ein ,regulär‘-mögliches Korollar im 2. Beweisgang aus Term :16: ''':''' <span style="color:#4C58FF">— ‚'''Gy→Yy'''‘ —</span>, mit der <span style="color:#4C58FF">[ Instanz(Y:=E<sub>not</sub>) ]</span>, und der <span style="color:#4C58FF">[ FUB(y:=x) ]</span>; und auch ein ,regulär‘-mögliches Korollar im 3. Beweisgang ''':''' entsprechend der <span style="color:#FF6000">„logischen Implikation”</span> :: <span style="color:#4C58FF">[├ A ├ B ╞ A → B ]</span> von Term :01: <span style="color:#4C58FF">— ‚'''Gx'''‘ — </span> und Term :05: <span style="color:#4C58FF"> —‚'''E'''<sub>not</sub>'''x'''‘ —</span> aus diesem Beweisgang. In Worten ''':''' <span style="color:#FF6000">»</span>''Angenommen, '' <span style="color:#4C58FF">— ‚'''x'''‘ —</span> ''steht für den GOTT der Christen'' ''':''' <span style="color:#4C58FF">— ‚'''G'''‘ — </span>, ''dann existiert dieser GOTT'', <span style="color:#4C58FF">— ‚'''x'''‘ —</span>, ''für uns notwendig'', <span style="color:#4C58FF">— <sub>not</sub> —</span> <span style="color:#FF6000">«</span>.)</span> Der Unendliche, GOTT, ist <span style="color:#FF6000">»''unabhängig von der zufälligen'' <span style="color:#00B000">[ Raum-Zeit-]</span>''Struktur''«</span> unserer ‚vergänglichen‘, ,endlichen‘ Welt, welche prinzipiell vom dreidimensionalen Raum und von der unwiederbringlich ‚vergehenden‘ Zeit geprägt ist. Der ,GOTT der Christen‘ ist <span style="color:#FF6000">»''unabhängig''«</span> von dieser <span style="color:#FF6600">„vergehenden Raum-Zeit“, — »''jenes rätselhafte und anscheinend in sich widersprüchliche Etwas''« <span style="color:#00B000">(GÖDEL)<ref>Kurt GÖDEL, ‚''<span style="font-family: Times;"><big>Eine Bemerkung über die Beziehungen zwischen der Relativitätstheorie und der idealistischen Philosophie‘</big></span>'', in P.A.SCHILPP (Hg.): ‚''<span style="font-family: Times;"><big>Albert Einstein, Philosoph und Naturforscher‘</big></span>'', Seite 406</ref></span> —</span>. Ohne ‚Zeit‘ gibt es keinen zeitlichen Unterschied zwischen ‚Ursache‘ und ‚Wirkung‘, <span style="color:#00B000">(beides ist zeitlos ,eins‘)</span>, und so ist der zeitlos-ewige GOTT, der <span style="color:#FF6000">»''notwendig aus sich ,existiert‘'' «</span> ''':''' <span style="color:#4C58FF">— ‚'''□∃xGx'''‘ — </span>, <span style="color:#FF6000">„unverursacht“ <span style="color:#00B000">|</span> „ursprungslos“</span> für uns immer schon ‚da‘ ''':''' <span style="color:#00B000">(GÖDEL — ohne religiöses Bekenntnis)</span>.
Mit dem GÖDEL-Kalkül ist die <span style="color:#FF6000">„Rede von GOTT“</span> auf eine ‚vernünftige Basis‘ gestellt worden, und ist somit für jeden Menschen nachvollziehbar, <span style="color:#FF6000">»''rein verstandesmäßig, (ohne sich auf den Glauben an irgendeine Religion zu stützen)''«</span>, wie obige Beispiele zeigen.
'''Resümee :'''
Das GÖDEL-Kalkül zeigt mit <span style="color:#FF6000">»''mathematischer Evidenz''«</span>, was notwendig folgt, wenn die Axiome ‚wahr‘ sind, <span style="color:#00B000">(die Axiome bilden formal-syntaktisch <span style="color:#FF6000">»''die theologische Weltanschauung''«</span> ab)</span>, unter der Voraussetzung, dass die Axiome <span style="color:#FF6000">»''unabhängig von der zufälligen'' <span style="color:#00B000">[ Raum-Zeit-]</span>'' Struktur''«</span> unserer Welt sind. Die ,Verifikation‘ der Axiome und Definitionen von GOTT und seiner Vollkommenheiten gelingt GÖDEL, — entsprechend seiner Unabhängigkeits-Bedingung —, durch den Aufweis ihrer Widerspruchsfreiheit ''':''' sie sind somit ,wahr‘ und, — im Kontext einer <span style="color:#FF6000">»''theologischen Weltanschauung''«</span> —, auch ,annehmbar‘ in unserer ,realen‘ Welt ''':''' <span style="color:#00B000">(siehe Anhang, 2. Beweisgang und Anmerkung-2)</span>. Er vermeidet damit den Fehler, der immer wieder im Diskurs über Gottesbeweise gemacht wird ''':''' GOTT mit seinen Geschöpfen zu vergleichen. Diese logisch-philosophische Rede von GOTT <span style="color:#00B000">(<span style="color:#FF6000">»''ohne sich auf den Glauben an irgendeine Religion zu stützen''«</span>)</span> hat eine <u>mehr</u> als zweitausendjährige Tradition hinter sich. Der <span style="color:#FF6000">„100-Taler-Gott“</span> des Philosophen KANT, hat heute, nachdem der Logiker und Systemtheoretiker GÖDEL sein System vorgelegt hat, an ‚Strahlkraft‘ verloren.
Kurt GÖDEL ''':'''
::<span style="color:#FF6000">» ''Die theologische Weltanschauung'', <span style="color:#00B000">[ dass GOTT für uns immer schon ‚da‘ ist ]</span>, ''ist rein verstandesmäßig mit allen bekannten Tatsachen durchaus vereinbar'';«</span> <span style="color:#00B000">[ d.h. sie ist das ,Resultat‘ der, — vom Glauben geleiteten —, ‚theoretischen Vernunft‘, alias ‚reinen Vernunft‘, und nicht bloß das ‚Postulat‘ einer ‚praktischen Vernunft‘, wie KANT meint ]. <span style="color:#FF6000">»''Der'' <span style="color:#00B000">[ christliche ]</span> ''Glaube ist die ‚Pupille‘ im ‚Auge‘ unseres Verstandes.''«</span> (Heilige KATHARINA von Siena, Lehrerin der Kirche, Patronin Europas<ref>vgl. <span style="font-family: Times;"><big>''Gebet 7 ‚Für die neuen Kardinäle‘, Rom, 21. Dezember 1378,''</big></span> aus <span style="font-family: Times;"><big>''Caterina von Siena ,Die Gebete‘.''</big></span> Kleinhain 2019, online: https://caterina.at/werke/gebete/gebete-detailansicht/gebet-7.html</ref> )</span>
Der sonst so rationale KANT, hier doch etwas emotionell, <span style="color:#00B000">(als wolle er die Ergebnisse im GÖDEL-Kalkül nicht wahr haben, die belegen, dass er sich bei GOTT geirrt, und die Funktion des christlichen Glaubens für die Philosophie falsch eingeschätzt hat)</span> ''':'''
::<span style="color:#FF6000">»</span> ''Es war etwas ganz Unnatürliches und eine bloße Neuerung des Schulwitzes, aus einer ganz willkürlich entworfenen Idee das Dasein des ihr entsprechenden Gegenstandes selbst ausklauben zu wollen''<ref>vgl. ‚''<span style="font-family: Times;"><big>Kritik der reinen Vernunft</big></span>''‘, Seite 403. https://www.korpora.org/kant/aa03/403.html</ref>.<span style="color:#FF6000">«</span>
Für KANT, für die Scholastiker, <span style="color:#00B000">(und auch für uns)</span>, ist es natürlich ‚logisch‘, dass aus einem als ‚möglich’ gedachten Begriff, <span style="color:#FF6000">»</span>''aus einer ganz willkürlich entworfenen Idee''<span style="color:#FF6000">«</span>, keine Existenzaussage abgeleitet werden kann. <span style="color:#00B000">(Aus dem bloß gedachten Begriff ,goldene Berge‘ folgt natürlich nicht, dass es solche in Wirklichkeit auch gibt.)</span> In der philosophisch-<span style="color:#4C58FF">,theologischen’</span> Tradition, die von ARISTOTELES herkommt, ist der Begriff <span style="color:#FF6000">»''GOTT''«</span> jedoch von allen anderen Begriffen so verschieden, so dass für GOTT diese Logik KANTS nicht mehr gilt. GOTT ist ,unvergleichlich‘ und ,einzigartig‘.
Dazu der Kommentar von HEGEL ''':'''
::<span style="color:#FF6000">»''Wenn KANT sagt, man könne aus dem Begriff'' <span style="color:#00B000">[ ‚GOTT‘ ]</span> ''die Realität nicht ,herausklauben‘, so ist da der Begriff als endlich gefasst''.« <span style="color:#00B000">[ In der Endlichkeit unserer Welt trifft die Logik KANTS zu, dass dem ‚Begriff‘ nicht ,notwendig‘ das ‚Sein‘ folgt, denn es gibt in ihr die ,Lüge‘, die das ,Wirklich-Sein‘ im Begriff bloß behauptet, ohne dass es ,in Wirklichkeit‘ zutrifft, was sie behauptet. Es gilt hier nach KANT ''':''' »''Sein ist kein reales Prädikat''«. Somit ist ]</span> »''...der Begriff ohne'' <span style="color:#00B000">[ reales ]</span> ''Sein ein Einseitiges und Unwahres, und ebenso das Sein, in dem kein Begriff ist'', <span style="color:#00B000">[ ist ]</span> ''das begrifflose Sein,'' <span style="color:#00B000">[ d.i. das relative ,Noch-Nicht-Begriffene‘ ]</span>.'' Dieser Gegensatz, der in die Endlichkeit fällt'' <span style="color:#00B000">[ im Endlichen zutrifft ]</span>, ''kann bei dem Unendlichen, GOTT, gar nicht statthaben''<ref>Georg Wilhelm Friedrich HEGEL, ‚<span style="font-family: Times;"><big>''Ausführungen des ontologischen Beweises''</big></span>‘ in den ‚<span style="font-family: Times;"><big>''Vorlesungen über die Philosophie der Religion vom Jahr 1831''</big></span>‘ . Hamburg 1966, Seiten 175 bzw. 174</ref>; <span style="color:#00B000">[ denn ,Begriff‘, bzw. ,Denken‘ und ,Sein‘ sind in dem Unendlichen, GOTT, untrennbar und real immer dasselbe. Auf Grund dieser ontologischen Identität ,personifiziert‘ und ,repräsentiert‘ GOTT die ,Wahrheit‘ ''':''' GOTT ist die ,Wahrheit‘. In GOTT, dem <span style="color:#FF6000">„Schöpfer der Welt“</span>, folgt dem ,Begriff‘ immer ,notwendig‘ das ,Sein‘ ''':''' <span style="color:#CC66FF">»''GOTT sprach ''':''' Es werde ,Licht‘. Und es wurde Licht''«, <small>{{Bibel | Genesis |1|3|EU}}</small>;</span> oder auch ''':''' <span style="color:#CC66FF">»''Der Herr sprach, und sogleich geschah es; er gebot, und alles war da''«,</span> <small>{{Bibel | Psalm |33|9|EU}}</small>.]</span>«</span>
Das Entscheidende bei der <span style="color:#4C58FF">„theologischen“</span> Interpretation des GÖDEL-Kalküls ist, dass der <span style="color:#00B000">(Begriff)</span> GOTT der Christen ''':''' <span style="color:#4C58FF">— ‚'''Gx'''‘ —</span>, nicht auf die Ebene seiner ,endlichen‘ Geschöpfe und unserer Welt gestellt wird, <span style="color:#00B000">(d.i. das ‚Universum‘ im ,Urknall‘, die ‚100-Taler‘, ein ‚Tsunami‘, auch ,einfache Modelle‘ von unserer Welt, etc.)</span>, und damit verglichen wird, sondern, dass der GOTT der Christen in seiner Einzigartigkeit und Besonderheit als <span style="color:#FF6000">»''der Unendliche''«</span> belassen und als <span style="color:#FF6000">»''unabhängig von der zufälligen'' <span style="color:#00B000">[ Raum-Zeit-]</span>''Struktur''«</span> unserer vergänglichen Welt, — als <span style="color:#FF6000">»''der Unvergleichliche''«</span> —, verstanden wird. <span style="color:#00B000">(Alle Kritiken des sog. ,ontologischen‘ Gottesbeweises übersehen die Einzigartigkeit und Besonderheit des <span style="color:#FF6000">»''Unendlichen''«</span>, und/oder wollen diese nicht ,wahr‘ haben.)</span> Auch THOMAS von Aquin ,verortet‘ den GOTT ANSELMS, — in seiner Kritik an dessen Theorem —, irrtümlich unter die ,Dinge‘ der uns umgebenden ,Natur‘, wenn er sagt ''':''' GOTT <span style="color:#FF6000">„<span style="font-family: Times;"><big>esse in rerum natura</big></span>“</span>, d.h. wörtlich, dass der GOTT ANSELMS <span style="color:#FF6000">»</span>''in der Natur der Dinge'' <span style="color:#00B000">(unserer Welt)</span> ''existiert''<span style="color:#FF6000">«</span>, und verkennt somit, — wie nach ihm auch KANT —, die ,Unvergleichlichkeit‘ GOTTES, <span style="color:#00B000">(vgl. STh I q.2 a.1 ad 2<ref>„Deus … illud quo maius cogitari non potest; non tamen propter hoc sequitur quod intelligat id quod significatur per nomen, esse in rerum natura; sed in apprehensione intellectus tantum.“ ——— »''GOTT ist'' (nach ANSELM) ''der, über den Größeres nicht mehr gedacht werden kann. Aber nicht deswegen, weil er'', (der Narr von Psalm 14.1, den ANSELM zitiert), ''das versteht, was durch diesen Namen,'' (d.h. mit dem Begriff ,GOTT‘, als der ,Größte‘ über allem, im Theorem ANSELMS), ''bezeichnet wird, folgt daraus'', (wie ANSELM meint), ''dass er auch versteht, dass er'', (der GOTT ANSELMS), ''auch in der ,Natur‘ der Dinge'' (unserer Welt) ''existiert''; <span style="color:#00B000">[ was ANSELM so nie gesagt hat, und auch nie ,gemeint‘ hat ]</span>. ''Daraus folgt nur, dass er'', (als ,GOTT‘), ''bloß in der Auffassung seines Verstandes'', (d.h. nur im Denken des Narren als ,Begriff‘), ''existiert.''« ——— Hier ,verortet‘ THOMAS einerseits den unendlichen GOTT, von dem das Theorem ANSELMS spricht, irrtümlich unter die endlichen Dinge der uns umgebenden ,Natur‘, was sachlich dem theologischen Theorem der Unvergleichlichkeit GOTTES widerspricht, der nicht unter die Dinge unserer Welt eingereiht werden darf. Anderseits verliert er dadurch auch den ,Blick‘ für die Außerordentlichkeit und Besonderheit GOTTES, dessen Natur völlig verschieden und unabhängig von der ,Natur‘ unserer raum-zeitlichen Welt ist. GÖDEL beweist jedoch, mit ANSELM, weil es notwendig, ohne Widerspruch, (»''bloß in der Auffassung unseres Verstandes''«), möglich ist, dass GOTT existiert, ist es korrekt, daraus auch mit Notwendigkeit zu folgern, dass der Glaube des Erzbischofs ANSELM, und der Glaube seiner Anvertrauten, von der Wirklichkeit GOTTES, widerspruchsfrei, logisch richtig, und damit rational sinnvoll ist; denn Möglichkeit und Wirklichkeit sind in GOTT koinzident ,eins‘. Das ist das Privilegium GOTTES allein, der einzigartig und unvergleichlich ist. Damit zeigt er auf, dass THOMAS die Unvergleichlichkeit und Einzigartigkeit GOTTES in seinem Vorhalt nicht bedacht hat; und außerdem ANSELM missverstanden hat.</ref>)</span>; jedenfalls hier in der Auseinandersetzung mit ANSELM. Dagegen spricht ANSELM im ,''<span style="font-family: Times;"><big>Proslogion</big></span>''‘, Seite 85f, nur von einem <span style="color:#FF6000">„<span style="font-family: Times;"><big>esse et in re</big></span>“</span> GOTTES, d.h. dass GOTT ,auch in Wirklichkeit existiert‘, <span style="color:#FF6000">„<span style="font-family: Times;"><big>quod maius est</big></span>“</span>, was ,größer‘, bzw. ,mehr‘ ist, als <span style="color:#FF6000">„<span style="font-family: Times;"><big>esse solo in intellectu</big></span>“</span>, als nur ,im Verstand zu sein‘; wobei die ,zeitlose-überzeitliche‘ Wirklichkeit, <span style="color:#00B000">(<span style="color:#FF6000">„<span style="font-family: Times;"><big>re</big></span>“</span>, die Natur)</span>, GOTTES jedoch völlig verschieden und <span style="color:#FF6000">»''unabhängig von der zufälligen''«</span> Wirklichkeit, <span style="color:#00B000">(die ,Natur‘, <span style="color:#FF6000">„<span style="font-family: Times;"><big>natura rerum</big></span>“</span>)</span>, der ,raum-zeitlichen‘ Welt der Dinge ist. Daher ist sie mit dieser auch nicht vergleichbar. GOTT ist <span style="color:#FF6000">„vollkommen“</span> und alle <span style="color:#FF6000">„Vollkommenheiten“</span> in GOTT, <span style="color:#00B000">(die ultimativen ,Transzendentalia‘ und alle Zuschreibungen)</span>, sind koinzident ,eins‘, — ,fallen <span style="color:#FF6000">„notwendig“</span> perspektivisch in eins zusammen‘, und sind daher konvertierbar. Darum ist auch die Wirklichkeit, <span style="color:#00B000">(Natur)</span>, GOTTES ,einzigartig‘ und ,unvergleichlich‘.
Mit dem FUHRMANN-Korollar-3 ist die Exklusivität und Außerordentlichkeit GOTTES definitiv im Kalkül ,bewiesen‘ <span style="color:#00B000">( ╞ )</span>. Der abendländische Monotheïsmus ist somit eine ,logische‘ Konsequenz aus den GÖDEL-Axiomen. <span style="color:#00B000">(Das <span style="color:#4C58FF">„theologische“</span> Theorem von der ,Einzigartigkeit‘ und Exklusivität GOTTES, d.h. hier im Kontext ''':''' die exklusive Einheit von GOTT und Mensch in JESUS CHRISTUS; von Vater, Sohn und Geist im <span style="color:#4C58FF">„Dreifaltigen“</span>; die Einheit von Essenz und Existenz, von Begriff und Sein, von Ursache und Wirkung, von Subjekt und Objekt, von Möglichkeit und Wirklichkeit, und die unbegrenzte Summeneinheit und Koinzidenz der unendlichen Transzendentalien in GOTT, ist, — mit HEGEL —, die Voraussetzung und Bedingung jeder Philosophie ''':''' <span style="color:#FF6000">»''Die Einheit muss am Anfang der Philosophie stehen''«</span>; und ist zugleich auch ihr gesuchtes und bewiesenes Endergebnis und Ziel ''':''' <span style="color:#FF6000">»''Diese Einheit muss auch das Resultat der Philosophie sein''«</span><ref>https://hegel-system.de/de/gottesbeweis.htm#hegels-kritik-an-kant</ref>, was hier im GÖDEL-Kalkül ,logisch‘ mit Korollar-3 verifiziert wird ''':''' <span style="color:#4C58FF">— ‚'''□(∃xGx ∧ ∀y(Gy→x=y))'''‘ —</span> ''':''' <span style="color:#FF6000">»''GOTT ist exklusiv einzigartig''«</span>.)</span>
Die Einzigartigkeit GOTTES bedingt die Koinzidenz, den inneren Zusammenhang aller seiner Vollkommenheiten und Zuschreibungen, <span style="color:#00B000">(Axiom-2)</span>, d.h. ihr paarweise, perspektivisches ,Zusammenfallen in eins‘ im Unendlichen, GOTT. ——— Aus der Notwendigkeit aller positiven Eigenschaften und Zuschreibungen, <span style="color:#00B000">(d.h. aus den, an sich, notwendigen, ultimativen Transzendentalien, Axiom-4)</span>, die in GOTT paarweise, als ,Wesenseigenschaften‘ koinzident ,eins‘ sind, <span style="color:#00B000">(Definition-2)</span>, ist die Einzigkeit GOTTES für uns erschließbar, <span style="color:#00B000">(Korollar-3)</span>. Axiom-4 ist die erste, ,modal‘ <span style="color:#FF6000">„notwendige“</span>, d.h. die transzendentale Voraussetzung für Korollar-3.
Wenn im FUHRMANN-Korollar-3 das <span style="color:#4C58FF">— ‚'''x'''‘ —</span> z. B. für GOTT, dem ,Vater‘ der Christen, und das <span style="color:#4C58FF">— ‚'''y'''‘ —</span> für GOTT, dem ,Sohn‘, d.h. für ,JESUS CHRISTUS‘ steht, oder — zugleich — für den ,HEILIGEN GEIST‘, <span style="color:#00B000">(den ,Dreifaltigen GOTT‘ der Christenheit)</span>; oder auch zugleich für die Gottesbezeichnung ,GOTT-ADONAI‘ der Juden, oder zugleich für die Gottesbezeichnung ,ALLAH‘ der Muslime steht, dann weist dieses Korollar, für <span style="color:#4C58FF">— ‚'''∀y'''‘ —</span>, mit der ,ontologischen Identität‘ ''':''' <span style="color:#4C58FF">— ‚'''x=y'''‘ —</span>, auf die ,Koinzidenz‘ des <span style="color:#4C58FF">„Dreifaltigen“</span>, und auf die <span style="color:#4C58FF">„Hypostatische Union“</span> der Naturen von GOTT und Mensch in JESUS CHRISTUS, bzw. auch auf den inneren Zusammenhang der drei Buch-Religionen hin.
===<div class="center"><span style="color:#660066">Anhang : das GÖDEL-Kalkül</span></div>===
In der ,Legende zum GÖDEL-Kalkül‘ wird an einige Basics erinnert, und diese für die operative Praxis im anstehenden Kalkül, mit Hilfe von logischen Meta-Terme, adaptiert. Zusätzliche, notwendige Regeln und Gesetze werden dann ,benannt‘, wenn sie in der Kalkül-Durchführung auftreten.
{|class="wikitable"
|-
! <div class="center"><span style="color:#660066">Legende zum GÖDEL-Kalkül</span></div>
|-
|
<small>
<math>\begin{align}
{\color{blue}\text{ ◇}} \text{ :: konsistent ↔ widerspruchsfrei ↔ möglich ↔ denkbar, } & {\color{blue}\text{ □}} \text{ :: notwendig ↔ wirklich, für jede mögliche Welt ↔ exklusiv} \\
\text{logischer Meta-Term ::} {\color{blue}\text{ [ A ├ B ]}} \text{ ::} & \text{„aus A folgt im Kalkül ,regulär‘ (├ ) B.“} \\
\text{ A, B sind Aussagen über Eigenschaften, (A ist keine Eigenschaft);} & \text{die Aussage, z.B. in der Kalkül-Zeile 10, wird als ,Term :10:‘ bezeichnet} \\
{\color{blue}\text{ AE}} \text{ ::} & \text{Argument Einführung, Prämisse, Postulat } \\
{\color{blue}\text{ Xx}} \text{ ::} & \text{„X ist eine Eigenschaft der Individuum-Variable x.“ } \\
{\color{blue}\text{ ¬PX}} \text{ ::} & \text{„X ist keine positive Eigenschaft, ist keine Perfektion, ist nicht vollkommen.“ } \\
{\color{blue}\text{ Instanz(X := Y)}} \text{ ::} & \text{Substitution der Eigenschaft X durch die ,bestimmte‘ Eigenschaft Y } \\
\text{ (Eine ,Instanz‘ ist ein Exemplar aus einer Menge gleichartiger Dinge;} & \text{hier die ,bestimmte‘ Eigenschaft Y, als Ersatz für das unbestimmte X.) } \\
{\color{blue}\text{ FUB(x := y)}} \text{ ::} & \text{Freie-Um-Benennung der Individuum-Variable x in y } \\
\text{ ,modal‘-frei gewählte Kalkül-Prämisse ::} {\color{blue}\text{ Gx}} \text{ ::} & \text{„Die Variable x steht für den GOTT der Christen.“ } \\
{\color{blue}\text{ [ G(y) ├ ⱯyG(y) ]}}\text{ ::} & \text{All-Operator-Einführung der Variable y für GOTT } \\
\text{ „Angenommen, die Variable y steht für GOTT, dann} & \text{folgt ,regulär‘ (├ ), dass auch jedes y im Kalkül für GOTT steht.“}\\
{\color{blue}\text{[ ⱯXA(X) ├ A(X) ]}}\text{ ::} & \text{ All-Operator-Beseitigung für die substituierte Eigenschaft X } \\
\text{ „Wenn X durch eine ,bestimmte‘ Eigenschaft ,instanziiert‘ ist oder} & \text{wird, dann kann der All-Operator von X ,regulär‘ (├ ) beseitigt werden.“}\\
{\color{blue}\text{ KOMM(↔)}}\text{ ::} & \;{\color{blue}\text{[ (A ↔ B) ↔ (B ↔ A) ]}} \text{ :: Kommutativgesetz für (↔)}\\
{\color{blue}\text{ DIST(□∧)}}\text{ ::} & \;{\color{blue}\text{[ (□A ∧ □B) ↔ □(A ∧ B) ]}} \text{ :: Distributivgesetz für (□∧)} \\
\text{ (hypothetischer Syllogismus, häufige logische Schlussregel) ::} & \;{\color{blue}\text{[ A → B, A ├ B ]}} \text{ :: (Modus ponendo ponens) :: Abtrennregel.} \\
\text{ „Wenn es wahr ist, dass aus A ein B folgt, und wenn A wahr ist,} & \text{dann ist im Kalkül ,regulär‘ (├ ) ableitbar, dass auch B wahr ist.“} \\
\text{ (negativer hypothetischer Syllogismus) ::} & \;{\color{blue}\text{[ A → B, ¬B ├ ¬A ]}} \text{ :: (Modus tollendo tollens)} \\
\text{ „Wenn es wahr ist, dass aus A ein B folgt, und wenn B falsch ist,} & \text{dann ist im Kalkül ,regulär‘ (├ ) ableitbar, dass auch A falsch ist.“} \\
\text{''KONDITIONALER BEWEIS“ ::} & \;{\color{blue}\text{[ ├ A ├ B ╞ A → B ]}} \text{ :: (logische Implikation)} \\
\text{ „Angenommen, A ist ,regulär‘ Axiom oder Prämisse, und B ist im} & \text{Kalkül ,regulär‘ abgeleitet, dann ist ,bewiesen‘ ( ╞ ) : A impliziert B, ist wahr.“} \\
\text{''INDIREKTER BEWEIS“ ::} & \;{\color{blue}\text{[ ├ ¬A → F ╞ A ]}} \text{ :: (Reductio ad absurdum)} \\
\text{ „Wenn im Kalkül aus ¬A ,regulär‘ eine Kontradiktion F} & \text{folgt, dann ist A ,bewiesen‘ ( ╞ ) : A ist ,wahr‘.“} \\
\end{align}</math>
</small>
|}
A. FUHRMANN ''':''' <span style="color:#FF6000">»</span> ''Eine Prädikatenlogik zweiter Stufe ist eine Logik, in der die Quantoren auch Eigenschaftsausdrücke <span style="color:#00B000">(<span style="color:#FF6000">„Prädikate”</span>)</span> binden können''. <span style="color:#00B000">[ Die ,Prädikate‘ werden in einem Kalkül dieser Logik durch Definitionen ,bestimmt‘ ]</span>. ''Wir werden uns im folgenden recht frei einer dafür geeigneten formalen Sprache bedienen. Äußere Quantoren werden meist weggelassen und wir schreiben kurz'' <span style="color:#4C58FF">— ‚'''Xx'''‘ — </span> ''bzw.'' <span style="color:#4C58FF">— ‚'''PX'''‘ — </span> ''um auszudrücken, dass das Individuum'' <span style="color:#4C58FF">— ‚'''x'''‘ — </span> ''die Eigenschaft'' <span style="color:#4C58FF">— ‚'''X'''‘ — </span> ''hat, bzw. dass die Eigenschaft'' <span style="color:#4C58FF">— ‚'''X'''‘ — </span> ''die höherstufige Eigenschaft'' <span style="color:#4C58FF">— ‚'''P'''‘ — </span> ''<span style="color:#00B000">(für <span style="color:#FF6000">„positiv”</span>)</span> hat;'' <span style="color:#00B000"> [ wobei die Eigenschaft <span style="color:#4C58FF">— ‚'''P'''‘ — </span> als einzige im Kalkül ,unbestimmt‘ bleibt ]</span>. <span style="color:#FF6000">«</span><ref>A. FUHRMANN ‚''<span style="font-family: Times;"><big>‚G‘ wie Gödel. Kurt Gödels axiomatische Theologie</big></span>''‘, Seite 6, Anmerkung 3. Konform mit seinem Artikel in ‚''<span style="font-family: Times;"><big>Logik in der Philosophie</big></span>''‘ hg. v. P. SCHROEDER-HEISTER, W. SPOHN und E. OLSSON. 2005, Synchron, Heidelberg.</ref>
Der All-Quantor für Eigenschaften, hier im GÖDEL-Kalkül der ,modalen‘ Prädikatenlogik zweiter Stufe, bindet die ,unbestimmte‘ Eigenschafts-Variable <span style="color:#4C58FF">— ‚'''X'''‘ —</span> ausschließlich nur in den Definitionen im 2. und 3. Beweisgang; <span style="color:#00B000">(im ersten Beweisgang gibt es keine Definition)</span>. Wobei diese Definitionen auch nur dann <span style="color:#FF6000">„wahr und widerspruchsfrei”</span> sind, wenn ihre Eigenschafts-Variablen ausschließlich mit ,Transzendentalien‘ belegt werden. Andernfalls sind sie sofort <span style="color:#FF6000">„modal falsch”</span>. Dieser All-Quantor wird dann jedes Mal in der Beweis-Durchführung durch die Substitution ''':''' <span style="color:#4C58FF"> [ Instanz(X:= ..) ]</span> mit ,bestimmte‘ Eigenschafts-Konstanten wie <span style="color:#4C58FF">— (X:= G) —</span>, bzw. <span style="color:#4C58FF">— (X:= ¬Y) —</span>, oder <span style="color:#4C58FF">— (X:= E<sub>not</sub>) —</span> ,regulär‘ <span style="color:#00B000">(├ )</span> beseitigt ''':''' <span style="color:#4C58FF"> [ ⱯXA(X) ├ A(X) ]</span>; wobei die Eigenschafts-Konstante im Kalkül entweder als Zwischenergebnis ,regulär‘ abgeleitet, <span style="color:#00B000">(,errechnet‘)</span>, oder mit einer Definition schon ,bestimmt‘ worden ist.
Die spezifische ‚Eigenschaft‘ einer Eigenschaft wird hier, in der formalen Syntax der ,modalen‘ Prädikatenlogik zweiter Stufe, als eine tiefer gestellte Abkürzung <span style="color:#00B000">(als Index)</span> an ihre Trägereigenschaft angehängt, wie z. B. ‚wesentlich‘, bzw. ‚essentiell‘ durch <span style="color:#4C58FF"> — <sub>ess</sub> —</span>, oder ‚notwendig‘ durch <span style="color:#4C58FF"> — <sub>not</sub> —</span>. In der Definition-3 steht der Term ''':''' <span style="color:#4C58FF"> —‚'''E'''<sub>not</sub>'''x'''‘ —</span>, um auszudrücken, dass das Individuum <span style="color:#4C58FF">— ‚'''x'''‘ —</span> notwendig <span style="color:#4C58FF">— <sub>not</sub> —</span> die Eigenschaft ''':''' <span style="color:#4C58FF">— ‚'''E'''‘ —</span>, für ,Existenz‘, hat, d.h. <span style="color:#FF6000">»''das <span style="color:#4C58FF">‚'''x'''‘</span> existiert notwendig''«</span>. Der schon von GÖDEL indizierte Term ''':''' <span style="color:#4C58FF">—‚'''G'''<sub>ess</sub>'''x'''‘ —</span> kann gelesen werden als ''':''' <span style="color:#FF6000">»''Das Individuum <span style="color:#4C58FF">‚'''x'''‘</span> hat die Wesenseigenschaft, <span style="color:#4C58FF"> — <sub>ess</sub> — </span> ''':''' GOTT zu sein, <span style="color:#4C58FF">‚'''G'''‘ </span>''«</span>, statt der ,an sich‘ konformen, aber <span style="color:#4C58FF">„theologisch“</span> etwas ungenauen Formulierung ''':''' <span style="color:#FF6000">»''das'' <span style="color:#4C58FF">‚'''x'''‘</span> ''ist wesentlich göttlich''«</span>; oder mit der Voraussetzung ''':''' <span style="color:#4C58FF">— ‚'''Gx'''→'''G'''<sub>ess</sub>'''x'''‘ — </span> deutlicher und <span style="color:#4C58FF">„theologisch“</span> korrekt ''':''' <span style="color:#FF6000">»''Wenn'' <span style="color:#4C58FF">‚'''x'''‘</span> ''für den GOTT der Christen'', <span style="color:#4C58FF">‚'''G'''‘</span>, ''steht, dann ist GOTT-Sein'', <span style="color:#4C58FF">‚'''G'''‘</span>, <span style="color:#00B000">(,Existenz‘)</span> ''das Wesen dieses GOTTES'', <span style="color:#4C58FF">— <sub>ess</sub>‚'''x'''‘ —</span>, <span style="color:#00B000">(,Essenz‘)</span>«</span>; wobei, — entsprechend der ,methodologischen‘ Prämisse des Kalküls <span style="color:#00B000">(<span style="color:#4C58FF">— ‚'''Gx'''‘ — </span> ''':''' das <span style="color:#4C58FF">‚'''x'''‘</span> steht für den ,GOTT der Christen‘)</span> —, bei der Interpretation der Terme dieses besonderen Kalküls, die <span style="color:#4C58FF">„christliche Theologie”</span> für den Begriff <span style="color:#FF6000">„GOTT”</span>, Korrektur und die leitende Instanz ist. Dabei muss die Dreifach-Äquivalenz von <span style="color:#4C58FF"><span style="color:#4C58FF">— ‚'''G'''‘ — </span></span>berücksichtigt werden. Welche der drei Äquivalenzen, bzw. Lesearten von <span style="color:#4C58FF">— ‚'''G'''‘ —</span> bei einem bestimmten Term im Kalkül zulässig ist, muss <span style="color:#4C58FF">„theologisch”</span> überprüft und evaluiert werden. Bei manchen können sogar alle drei Lesearten <span style="color:#4C58FF">„theologisch”</span> zulässig sein.
Warum es in der modalen Prädikatenlogik zweiter Stufe, im GÖDEL-System, ,Definitionen‘ gibt, erklärt GÖDEL formal-syntaktisch ''':''' <span style="color:#FF6000">»''Definitionen dienen ja nur der kürzeren Schreibwelse und sind daher prinzipiell überflüssig.''«</span> <span style="color:#00B000">(Aus der Anmerkung 6 in seinem Hauptwerk ''':''' ,''<span style="font-family: Times;"><big>Über formal unentscheidbare Sätze der Principia Mathematica und verwandter Systeme I</big></span>''‘.)</span> Für die <span style="color:#4C58FF">„theologische”</span> Interpretation und Hermeneutik seines Kalküls, sind die ,Definitionen‘ im GÖDEL-System jedoch wesentlich, und letztlich entscheidend. Erst dadurch wird sein Axiomensystem zur <span style="color:#4C58FF">„axiomatischen Theologie”</span>. GÖDEL gibt selbst den hermeneutischen Hinweis dazu, wenn er über den höherstufigen Eigenschaftsbegriff <span style="color:#FF6000">»''positiv''«</span> sagt, der nur ,indirekt‘ definiert wird, jedoch mit seinem gesamten Axiomensystem ''':''' <span style="color:#FF6000">»''Positiv bedeutet positiv im moralisch ästhetischen Sinne, (unabhängig von der zufälligen Struktur der Welt). Nur dann sind die Axiome wahr''.«</span> Die Deutung dieses ,höherstufigen‘ Begriffs als ein anderes Wort für <span style="color:#FF6000">„Transzendentalia“</span> ergibt sich aus der <span style="color:#4C58FF">„theologischen”</span> Evaluierung, d.h. der Einordnung des GÖDEL-Systems in die ,laufende‘ <span style="color:#FF6000">„Rede von GOTT“</span>, seit Menschen, sich über den Sinn der Welt und unseres Lebens in ihr, Gedanken machen. GOTT, der ,transzendent‘, — unabhängig —, zur Welt ist, ist Thema im Theorem ANSELMS, um das es ja im GÖDEL-Kalkül geht. Eine <span style="color:#4C58FF">„theologische”</span> Hermeneutik des GÖDEL-Systems ist daher legitim, die, als ein Bemühen um ein überzeugendes Verstehen, aus dem <span style="color:#4C58FF">„theologischen”</span> Vorwissen vieler Generationen schöpft. Dieses ,Vorwissen‘ ist ,vorgängig‘ zum GÖDEL-System, und hat daher, — ,sachlich‘, und nicht nur ,zeitlich‘, — Priorität zum syntaktischen System GÖDELS; und ist damit legitim auch Interpretations-Vorgabe. <span style="color:#4C58FF">„Theologie”</span>, als Wissenschaft von GOTT, hat hier Interpretations-Vorrang.
Um philosophische, und sogar <span style="color:#4C58FF">„theologische”</span> Theoreme exakt zu formulieren, und untersuchen zu können, hat der Ausnahmelogiker GÖDEL, im 20. Jahrhundert, ein Tor aufgestoßen, das uns ermöglichen kann, <span style="color:#FF6000">»''mathematisch evident''«</span>, und logisch objektiv nachprüfbar, in diesen Disziplinen zu argumentieren. Mit seiner modalen Prädikatenlogik zweiter Stufe, <span style="color:#00B000">(mit den Definitionen, speziell ,zugeschnitten‘ auf seinen ,ontologischen‘ Beweis)</span>, hat GÖDEL dem alten Wunsch eines Raimundus LULLUS, eines Gottfried Wilhelm LEIBNIZ, eines Immanuel KANT, und anderer, nach einer nachprüfbaren ,Universalsprache‘ in den Geisteswissenschaften, entsprochen; analog zur Mathematik, als Universalsprache in den Naturwissenschaften. Der sog. ,Theorembeweiser‘ der Wissenschaftler Christoph BENZMÜLLER und Bruno WOLTZENLOGEL-PALEO, mit Hilfe eines Computers, im 21. Jahrhundert, ist die kongeniale und logische Folge aus diesem Quanten-Schritt GÖDELS.
In der folgenden Neu-Kalkülisierung, wird jeder einzelne operative Logik-Schritt des Kalküls in der '''linken Spalte''' nummeriert und als Term-Ergebnis angezeigt, und in der '''rechten Spalte''' werden die dafür benötigten Term-Komponenten und die dabei angewendeten Logik-Regeln und -Gesetze, mit Meta-Terme, dokumentiert. Am Anfang stehen die Ressourcen und das angestrebte Ziel des Beweisganges, <span style="color:#00B000">(das Theorem)</span>. Die GÖDEL Axiome und Definitionen, die Theoreme und Korollare, die Zwischenergebnisse, das Endergebnis, und die logischen Meta-Terme, <span style="color:#00B000">[ in eckigen Klammern ]</span>, werden kontextabhängig, <span style="color:#00B000">(durch ''„Benennungen“'')</span>, interpretiert, <span style="color:#00B000">(angezeigt durch ,Interpretationspunkte‘ — '''::''' —, falls nötig)</span>. Der jeweilige Beweisgang wird in den Anmerkungen ausführlich und umfassend kommentiert. Die Kalkül-Prämissen, <span style="color:#00B000">(<span style="color:#4C58FF">'''AE:'''</span> Argument Einführung)</span>, sind der modal-frei gewählte Einstieg in das Kalkül. Sie dokumentieren, zusammen mit dem angestrebten Beweis-Ziel, eine bestimmte Problemlage in einem externen Diskurs, der mit dem modalen Logik-System hier, formal-syntaktisch überprüft, und gegebenenfalls, verifiziert oder falsifiziert werden soll. Korollare sind einfache, logische Folgerungen aus dem jeweiligen Beweisgang. Theoreme und Korollare sind ,modal‘ gleichwertig.
====<div class="center"><span style="color:#660066">1. Beweisgang</span></div>====
{|class="wikitable"
|-
! <div class="center"><span style="color:#660066">GÖDELS ontologischer Beweis für Theorem 1, (Möglichkeitsbeweis)</span></div>
|-
! <span style="color:#00B000">''Terme der erweiterten Prädikatenlogik zweiter Stufe__________ „Benennungen“ und durchgeführte Logik-Operationen''</span>
|-
|
<small>
<math>\begin{align}
\text{(Axiom 1.1)} & \quad P \neg X \;\Longrightarrow\;\ \neg P\ X\ & \ & \text{„Wenn die Negation von X positiv ist, dann ist die Eigenschaft X nicht positiv“} \\
\text{(Axiom 2)} & \quad (P\ X \wedge \;\Box \;\forall x (\ X\ x \Longrightarrow \ Y\ x)) \Longrightarrow \ P\ Y & \ & \text{„Die Eigenschaften Y, die aus einer positiven Eigenschaft X modal} \\
\text{ } & \quad & \ & \; \; \text{notwendig folgen, sind auch positive Eigenschaften“} \\
\text{(Axiom 3)} & \quad P\ G \ & \ & \text{„Göttlichkeit, GOTT-Sein, ist eine pos. Eigenschaft“ ↔ „GOTT ist perfekt“} \\
\text{[RK]} &\quad \Box\; \forall x A \;\Longrightarrow\;\Box\; \forall x B\; \text{ ├ }\;\Box \; \forall x (A \Longrightarrow\; B)\ & \ & \; \text{( :: Modales Prinzip KRIPKE-Γ-Ɐx)} \\
\text{(Theorem 1)} &\quad P\ X \;\Longrightarrow\; \Diamond \; \exists x \ X \ x \ & \ & \text{ (◇ :: „möglich“ ↔ „konsistent“ ↔ „denkbar“; □ :: „notwendig“) } \\
\text{ } & \text{„Positive Eigenschaften sind konsistent“} & \ & \Longleftarrow\; \text{was zu beweisen ist !} \\
\text{01} & \quad P\ X \ & \ & \text{ AE: „Angenommen, es gibt positive Eigenschaften, Perfektionen“} \\
\text{02} & \quad P\ X \;\Rightarrow\; \neg \Diamond \; \exists x \ X \ x \ & \ & \text{ AE: „Angenommen, positive Eigenschaften sind nicht konsistent“} \\
\text{03} & \quad (\neg x = .. )\ & \ & \text{ AE: „Es gibt die Eigenschaft, nicht mit x identisch zu sein“ :: (ungleich)} \\
\text{04} & \quad (\ x = .. )\ & \ & \text{ AE: „Es gibt die Eigenschaft, mit x identisch zu sein“ :: (gleich)} \\
\text{05} & \quad \text{ ├ }\; \neg \Diamond \; \exists x \ X \ x \ & \ & \text{:01:02:[Modus ponendo ponens] :: [A, A→B├ B] :AE:} \\
\text{06} & \quad \neg\neg \Box \neg \exists x \ X \ x \ & \ & \text{:05:[ ◇A ↔ ¬□¬A] :: (Modalregel)} \\
\text{07} & \quad \neg\neg \Box \neg\neg \forall x \neg X \ x \ & \ & \text{:06:[∃xA ↔ ¬Ɐx¬A] :: (Quantoren Regel)} \\
\text{08} & \quad \text{ ├ }\; \Box \; \forall x \neg X \ x \ & \ & \text{:07:NEG :: [¬¬A↔A] :: (Gesetz der Aussagenlogik)} \\
\text{09} & \quad \Box \; \forall x \neg X \ x \Leftrightarrow\ W & \ & \text{:02:08:[(:02:↔W) → (├:08:↔W)] :: (Kalkülregel)} \\
\text{10} & \quad \Box \; \forall x \ X \ x \Leftrightarrow\ F & \ & \text{:09:[(¬A↔W)↔(A↔F)] :: (Regel für Wahrheitswerte)} \\
\text{11} & \quad \Box \; \forall x (\neg x = x ) \Leftrightarrow \ F \; \ & \text{ } & \text{□ⱯxXx:03:Instanz(X:=(¬x=..)) ⇒ Kontradiktion !} \\
\text{12} & \quad \Box \; \forall x (\ X \ x \Rightarrow\; (\neg x = x)) & \text{ } & \text{:10:11:[(:10:↔F) → (:11:↔F)] :: „ex falso sequitur quotlibet“ :[RK]} \\
\text{13} & \quad \ P\ X \wedge \;\Box \; \forall x (\ X \ x \Rightarrow\; (\neg x = x)) & \ & \text{:01:12:[Konjunktion] :: [A, B ├ A∧B]} \\
\text{14} & \quad \ (P\ X \wedge \;\Box \; \forall x (\ X \ x \Rightarrow \; (\neg x = x))) \Rightarrow \; P (\neg x = .. ) & \ & \text{(A2):Instanz(Y:=( ¬x= ..)) :: (Substitution für Eigenschaften)} \\
\text{15} & \quad \ P (\neg x = .. ) & \ & \text{:13:14:[Modus ponens] :: (logische Schlussregel)} \\
\text{16} & \quad \ P (\neg x = .. )\;\Rightarrow\ \neg P (\ x = .. )\ & \ & \text{(A1.1):Instanz(X:=(x=..))}\\
\text{17} & \quad \neg P (\ x = .. )\ & \ & \text{:15:16:[Modus ponens]}\\
\text{18} & \quad \Box \; \forall x \ (x = x ) \Leftrightarrow \ W \; \ & \ & \text{□ⱯxXx:04:Instanz(X:=(x=..)) ⇒ Tautologie !} \\
\text{19} & \quad \Box \; \forall x (\ X \ x \Rightarrow\; (x = x)) & \text{ } & \text{:10:18:[(:10:↔F) → (:18:↔W)] :: „ex falso sequitur etiam verum“ :[RK]} \\
\text{20} & \quad \ P\ X \wedge \;\Box \; \forall x (\ X \ x \Rightarrow\; (x = x)) & \ & \text{:01:19:[Konjunktion] } \\
\text{21} & \quad \ (P\ X \wedge \;\Box \; \forall x (\ X \ x \Rightarrow\; (x = x))) \Rightarrow \ P (x = .. ) & \ & \text{(A2):Instanz(Y:=(x=..))} \\
\text{22} & \quad \ P (\ x = .. )\ & \ & \text{:20:21:[Modus ponens]}\\
\text{23} & \quad \text{ ├ }\; (\neg P (\ x = .. )\ \wedge \ P (\ x = .. )) \Leftrightarrow\ F & \ & \text{:17:22:[Konjunktion] ⇒ Kontradiktion !}\\
\text{24} & \quad \neg \Diamond \; \exists x \ X \ x \Rightarrow (\neg P (\ x = .. )\ \wedge \ P (\ x = .. )) & \ & \text{:05:23:[├A├B╞ A→B] :: ''KONDITIONALER BEWEIS''}\\
\text{25} & \quad \neg\neg \Diamond \; \exists x \ X \ x & \ & \text{:24:23:[Modus tollendo tollens] :: [A→B,¬B ├ ¬A]}\\
\text{26} & \quad \text{ ├ }\; \Diamond \; \exists x \ X \ x & \ & \text{:25:NEG; bzw. :05:23:[├¬A→F ╞ A] :: ''INDIREKTER BEWEIS''}\\
\text{27} & \quad \ P\ X \;\Longrightarrow\; \Diamond \; \exists x \ X \ x \ & \ & \text{:01:26:[├A├B ╞ A→B]} \\
\text{(Theorem 1)} & \;\text{„Positive Eigenschaften sind konsistent“} & \ & \Longleftarrow\; \text{was zu beweisen war !} \\
\text{28} & \quad \ P\ G \;\Longrightarrow\; \Diamond \; \exists x \ G \ x \ & \ & \text{:27:Instanz(X:=G) } \\
\text{29} & \quad \Diamond \; \exists x \ G \ x \ & \ & \text{(A3):28:[Modus ponens]} \\
\text{(Korollar 1)} & \;\text{„Das Dasein GOTTES ist definitiv denkbar“} & \ & \text{„Es ist widerspruchsfrei möglich, dass es GOTT gibt“} \\
\end{align}</math>
</small>
|}
Anmerkung-1 ''':''' <span style="color:#00B000">(Der GÖDEL-Term <span style="color:#4C58FF">— ‚'''PX'''‘ —</span>, im Axiom-2 ist an sich überflüssig, da dieser hier als Prämisse :01: ohnehin ,angenommen‘ wird. Der Beweisgang kommt mit Axiom-2 auch ohne diesen GÖDEL-Term zum selben Ergebnis, und verkürzt sich damit sogar um zwei Schritte ''':''' Zeile 13 und Zeile 20 sind dann unnötig.)</span>
Der Beweisgang geht mit Axiom-3 ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ —</span>, als Kalkül-Ressource, prinzipiell von der Existenz eines GOTTES aus. Mit der Prämisse :01: <span style="color:#00B000">(hier im 1. Beweisgang)</span> postuliert GÖDEL vorerst allgemein, dass es <span style="color:#FF6000">»''Vollkommenheit, d.h. positive Eigenschaften''«</span> in unserer Welt gibt ''':''' <span style="color:#4C58FF">— ‚'''PX'''‘ —</span>, ohne hier zu definieren, was darunter zu verstehen ist. Definiert wird <span style="color:#00B000">(im 2. Beweisgang)</span>, was eine <span style="color:#FF6000">»''wesentliche Eigenschaft''«</span> ist ''':''' <span style="color:#4C58FF">— ‚'''X'''<sub>ess</sub>‘ —</span>, <span style="color:#00B000">(die <span style="color:#4C58FF">„theologisch“</span> als ,Transzendentalia‘ evaluiert wird)</span>; und mit Hilfe dieser Eigenschaft definiert GÖDEL <span style="color:#00B000">(im 3. Beweisgang)</span>, was eine <span style="color:#FF6000">»''notwendige Existenz''«</span> ist ''':''' <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>‘ —</span>, die er <span style="color:#00B000">(im selben Beweisgang)</span> axiomatisch mit den <span style="color:#FF6000">»''positiven Eigenschaften in GOTT''«</span> gleich setzt ''':''' Axiom-5 ''':''' <span style="color:#4C58FF"> —‚'''PE'''<sub>not</sub>‘ —</span>. Erst im 2. Beweisgang wird mit Term :13:, nach einer <span style="font-family: Times;"><big>,Reductio ad absurdum‘</big></span>, definitiv bewiesen <span style="color:#00B000">( ╞ )</span>, dass die, von GÖDEL, hier postulierten, <span style="color:#00B000">(allgemeinen)</span>, positiven Eigenschaften, <span style="color:#4C58FF">— ‚'''PX'''‘ —</span>, tatsächlich auch in GOTT <span style="color:#FF6000">»''positive Eigenschaften''«</span>, <span style="color:#4C58FF">— ‚'''PG'''‘ —</span>, sind; <span style="color:#00B000">(das sind die ultimativen ,Transzendentalia‘ in GOTT)</span>. Jetzt aber muss vorerst der ,Wunsch‘, bzw. die LEIBNIZ-Frage beantwortet werden ''':''' Ob, mit <span style="color:#FF6000">»''mathematischer Evidenz''«</span>, <span style="color:#FF6000">»''GOTT''«</span> ,möglich‘ ist, der, nach traditioneller Auffassung, <span style="color:#FF6000">»''ein Wesen von äußerster Größe und Vollkommenheit'' <span style="color:#00B000">[ ist ]</span>, ''das alle Grade derselben in sich schließt''«</span>, <span style="color:#00B000">(nach LEIBNIZ; was GÖDEL mit Definition-1 ,abbildet‘)</span>. Wenn man also beweisen will, dass die Existenz eines solchen ''<span style="color:#FF6000">»GOTTES«</span>'' ,möglich‘ sein soll, dann muss man beweisen, dass dieses postulierte System der <span style="color:#FF6000">»''positiven Eigenschaften in GOTT''«</span> möglich und formal ,widerspruchsfrei‘ ist. Das Ergebnis des 1. Beweisganges, das ,Theorem-1‘, <span style="color:#00B000">(,Erster Satz‘)</span>, fasst A. FUHRMANN zusammen als ''':''' <span style="color:#FF6000">»''Positive Eigenschaften sind konsistent''«</span>. Wenn sie nicht konsistent wären, käme es zu unlösbaren Widersprüchen, <span style="color:#00B000">(Term :24:)</span>. Term :27: ,besagt‘ ''':''' <span style="color:#FF6000">»''Es ist definitiv, d.h. ohne Widerspruch möglich, dass es positive Eigenschaften gibt''«</span>; denn die Behauptung, <span style="color:#00B000">(Prämisse :02:)</span>, es sei nicht möglich, dass es positive Eigenschaften, <span style="color:#00B000">(,Transzendentalia‘)</span>, in unserer Welt gibt, führt zu einem Widerspruch, <span style="color:#00B000">(Term :24:)</span>, und ist daher falsch und ,unlogisch’. Einmal Axiom-1 und zweimal Axiom-2, <span style="color:#00B000">(das die Gleichwertigkeit aller positiven Eigenschaften nachdrücklich klarstellt)</span>, sichern hier die Konsistenz <span style="color:#FF6000">»''aller positiven Eigenschaften in der Welt'', <span style="color:#00B000">[ die ,Transzendentalien‘ ]</span>, ''und damit auch die, die in GOTT sind''«</span>. <span style="color:#00B000">(Der 2. Beweisgang bestätigt, dass es in GOTT tatsächlich positive Eigenschaften gibt.)</span> Die ,Gleichwertigkeit‘ aller Transzendentalia, <span style="color:#00B000">(ihre ,Äquivalenz‘)</span>, ist formal-syntaktisch daran erkennbar, dass die beiden Eigenschafts-Variablen <span style="color:#4C58FF">— ‚'''X'''‘ —</span> und <span style="color:#4C58FF">— ‚'''Y'''‘ —</span> im Axiom-2 für beliebige, unterschiedliche ,positive‘ Eigenschaften gegenseitig austauschbar, <span style="color:#00B000">(,konvertierbar‘)</span>, sind. Das heißt, dass beliebige, unterschiedliche ,positive‘ Eigenschaften, für die diese Variablen stehen, sich paarweise, wechselseitig ,implizieren‘, einschließen, und damit notwendig voneinander abhängen, d.h. koinzident ,eins‘ sind, konvertierbar, und somit gleichwertig sind; entsprechend dem Theorem von den Transzendentalia. Zu Term :29:, dem Korollar zu Theorem-1, notiert GÖDEL am 10. Feb. 1970, <span style="color:#00B000">(übersetzt von Joachim BROMAND)</span> ''':''' <span style="color:#FF6000">»''◇∃xG(x) besagt, dass das System aller positiver Eigenschaften kompatibel ist'',</span> <span style="color:#00B000">[ d.h. miteinander verträglich, weil ohne Widersprüche ].</span> <span style="color:#FF6000">''Dies ist ,wahr‘ auf Grund von Axiom-2,'' <span style="color:#00B000">[ weil alle positiven Eigenschaften, d.h. die Transzendentalien, koinzident gleichwertig und konvertierbar sind ]</span>.«</span> Darum ist es definitiv ,möglich‘, dass es diesen GOTT gibt, der <span style="color:#FF6000">»''alle Grade der Vollkommenheit in sich schließt''«</span> und <span style="color:#FF6000">»''über dem ,Größeres‘ nicht mehr gedacht werden kann''«</span>, und, in weiterer Konsequenz, ist der GOTT-Glaube deshalb ,notwendig‘ widerspruchsfrei und sinnvoll, nach Theorem-3 ''':''' <u>Wenn</u> es ,ohne Widerspruch‘ ''<span style="color:#FF6000">»möglich, bzw. denkbar«</span>'' ist, dass es ''<span style="color:#FF6000">»GOTT«</span>'' gibt, <u>dann</u> folgt daraus ''<span style="color:#FF6000">»notwendig«</span>'' ''':''' es ist auch ,widerspruchsfrei‘, wenn man als Voraussetzung ,annimmt‘, dass es ''<span style="color:#FF6000">»GOTT wirklich, für jede mögliche Welt«</span>'' gibt ''':''' Term :11: im 3. Beweisgang. Der Wenn-Satz ist hier mit Korollar-1 bewiesen; der Dann-Satz wird im 3. Beweisgang bewiesen <span style="color:#00B000">( ╞ )</span>, d.i. die Widerspruchsfreiheit und Sinnhaftigkeit des GOTT-Glaubens.
Die ontologische ,Identität‘, d.h. die ,Gleichsetzung‘, bzw. die ,Koinzidenz‘ von Strukturen, die in der Endlichkeit für uns verschieden sind, jedoch in dem unbegrenzt Unendlichen, GOTT, paarweise, perspektivisch in eins zusammenfallen, wie ,Sein‘ und ,Wesen‘, wie ,Ursache‘ und ,Wirkung‘ usw., und auch die Äquivalenz und Austauschbarkeit der Transzendentalien, haben im GÖDEL-Kalkül die logisch-syntaktische Form einer, aus sich, ,modal‘ notwendigen Implikation zwischen zwei verschiedenen, gegenseitig austauschbaren Eigenschafts-Variablen ''':''' <span style="color:#4C58FF">— ‚'''□∀x(Xx→Yx)'''‘ —</span>. Dieses Term-Element stellt formal-syntaktisch die Gleichwertigkeit, <span style="color:#00B000">(Äquivalenz)</span>, bzw. die paarweise Koinzidenz aller positiven Eigenschaften und Zuordnungen in GOTT dar; sowohl hier im Axiom-2, als auch in der Definition-2 über die ,Wesenseigenschaften‘, im 2. Beweisgang, mit jeweils verschiedenen, frei umbenennbaren, Individuum-Variablen. Dieses Term-Element ,gilt‘, mit Notwendigkeit, allgemein, in allen Seienden ''':''' <span style="color:#4C58FF">— ‚'''□∀x'''‘ —</span>, jedoch nur für ihre ,positiven Eigenschaften‘, alias ,Transzendentalia‘, und wird sofort <span style="color:#FF6000">„modal falsch“</span>, wenn für <span style="color:#4C58FF">— ‚'''X'''‘ —</span>, oder <span style="color:#4C58FF">— ‚'''Y'''‘ —</span>, Eigenschaften eingesetzt werden, die, — von Natur aus —, weder positiv noch negativ sind, wie z. B. ,rot‘ oder ,schwer‘. Die wechselseitige Austauschbarkeit der noch ,unbestimmten‘ Eigenschafts-Variablen, für beliebige positive Eigenschaften, ist formal äquivalent zur freien Umbenennung der noch ,unbestimmten‘ Individuum-Variablen ''':''' <span style="color:#4C58FF">[ FUB(x:=y) ]</span>. Der formale, gegenseitige, allgemeine Austausch der positiven Eigenschafts-Variablen, bzw. die formale Gleichsetzung beliebiger positiver Eigenschaften, kann, auf Grund der Äquivalenz aller Vollkommenheiten, bzw. ,Transzendentalia‘, auch dann noch durchgeführt werden, wenn eine, von zwei Eigenschafts-Variablen, durch eine Definition oder eine Schlussfolgerung ,bestimmt‘ worden ist, und dadurch zu einer positiven Eigenschafts-Konstante, d.h. zu einer ,bestimmten‘, positiven Eigenschaft geworden ist. Das ist z. B. bei einer instanziierenden Substitution der Fall ''':''' <span style="color:#4C58FF">[ Instanz(X:=..) ]</span>. Das ist eine spezifische Eigenheit der GÖDEL-Axiomatik, weil alle relevanten, positiven Eigenschaften und Zuordnungen in GOTT, als <span style="color:#FF6000">„ultimative Transzendentalia“</span>, ,bestimmt‘ oder ,unbestimmt‘, immer auch miteinander kompatibel sind. Mit dem Term-Element ''':''' <span style="color:#4C58FF">— ‚'''□∀x(Xx→Yx)'''‘ —</span>, wird formal die <span style="color:#FF6000">„Koinzidenz“</span> aller ,Transzententalia‘ in der Welt, und auch der ,Zusammenfall‘ aller Gegensätze in GOTT, abgebildet.
Da die Variable <span style="color:#4C58FF">‚'''x'''‘</span> für GOTT steht, <span style="color:#4C58FF">‚'''G'''‘</span>, <span style="color:#00B000">(im Korollar-1)</span>, ist die Eigenschaft ''':''' ''<span style="color:#FF6000">„nicht mit x identisch zu sein“</span>'' ''':''' <span style="color:#4C58FF">(¬x=..)</span>, d.h. das <span style="color:#FF6000">„Ungleichsein“</span>, das <span style="color:#FF6000">„Anderssein“</span> GOTTES, <span style="color:#00B000">(Prämisse :03:)</span>, die entscheidende Voraussetzung und Norm für jeden Diskurs über GOTT ''':''' um der <span style="color:#FF6000">„Unvergleichlichkeit“</span>
GOTTES gerecht zu werden, darf GOTT niemals mit etwas aus der ''<span style="color:#FF6000">»zufälligen Struktur der Welt«</span>'' verglichen, d.h. gleich gesetzt werden. Der Term :18: <span style="color:#4C58FF">(x=x) ↔ W</span> erinnert an die Selbstbezeichnung des GOTTES-JHWH in Exodus 3,14 ''':''' <span style="color:#CC66FF">»''Ich bin der ‚Ich-Bin‘''«</span>. <span style="color:#00B000">(In der mystischen Tradition, in der ''<span style="font-family: Times;"><big>‚Legenda Maior‘</big></span>'' 92, über das Leben der Heiligen KATHARINA von Siena, Mystikerin und Lehrerin der Kirche, sagt GOTT-VATER zur KATHARINA ''':''' <span style="color:#FF6000">»''Weißt du, Meine Tochter, wer du bist und wer Ich bin'' '''?''' ''Du bist die, die nicht ist'', <span style="color:#00B000">[ aus dir selbst ]</span>, ''Ich aber bin derjenige, der IST'', <span style="color:#00B000">[ aus mir selbst ]</span>.«</span> Das ist eine wichtige, deutende Ergänzung, und die authentische <span style="color:#4C58FF">,theologische‘</span> Interpretation dieser Exodus Text-Stelle. Das ist der Grund für die <span style="color:#FF6000">„Unvergleichbarkeit“</span> GOTTES.)</span>
Zum Term :03: notiert A. FUHRMANN ''':''' <span style="color:#FF6000">»</span> ''Die Notation'' <span style="color:#4C58FF">(¬x=..)</span> ''für die Eigenschaft ''':''' <span style="color:#FF6000">„nicht mit x identisch zu sein“</span>'', <span style="color:#00B000">[ d.h. <span style="color:#FF6000">„Ungleichheit“, „Anderssein“</span>, bzw. die Notation <span style="color:#4C58FF">(x=..)</span> für den Existenzmodus-Perfektion ''':''' <span style="color:#FF6000">„Gleichheit“, „Idendität“</span> ]</span>, ''ist suggestiv und informell und ersetzt hier einen formal korrekten Abstraktionsausdruck wie'' <span style="color:#4C58FF">λy.(¬x=y)</span>, <span style="color:#00B000">[ bzw. <span style="color:#4C58FF">λy.(x=y)</span> ]</span>. ''Für die formal korrektere Notation bedarf es der zusätzlichen Vereinbarung, dass der Ausdruck'' <span style="color:#4C58FF">λy.(¬x=y)</span> ''gleichbedeutend sei mit dem Ausdruck'' <span style="color:#4C58FF">¬λy.(x=y)</span>. ''Diese Vereinbarung ist harmlos, da wir aufgrund der Regel der λ–Konversion'' ''':''' <span style="color:#4C58FF">λy.Xy.x ↔ Xx</span>, <span style="color:#00B000">[ mit der <span style="color:#4C58FF">Instanz(X:=(¬x=..))</span> ]</span>, ''so schließen dürfen'' ''':''' <span style="color:#4C58FF">λy.(¬x=y).x ↔ ¬x=x ↔ <span style="color:#00B000">¬(x=x)</span> ↔ ¬λy.(x=y).x</span> .<span style="color:#FF6000">«</span> <ref>A. FUHRMANN a.a.O. Seite 7, Anmerkung 4 (von mir korrigiert und ergänzt)</ref>
In der Kalkül-Zeile 29 wird das Möglichkeits-Korollar-1 durch einen <span style="color:#4C58FF">[ Modus ponens ]</span> mit Axiom-3 von der Kalkül-Prämisse-Term :01: ,abgekoppelt‘, d.h. es ist nicht mehr vom Term :01: <span style="color:#4C58FF">— ‚'''PX'''‘ —</span> logisch abhängig. Das Möglichkeits-Korollar-1 behält jedoch die bewiesene Widerspruchsfreiheit von Theorem-1, und ist damit nur mehr von Axiom-1 und Axiom-2 abhängig, was für das Theorem-ANSELMS am Schluss entscheidend ist. Erklärung zu Term :05: Das Ergebnis einer Logik-Operation zwischen Prämissen ist ,regulär‘ <span style="color:#00B000">(├ )</span> den Prämissen zuzurechnen.
Das Modale-Prinzip KRIPKE-Γ-Ɐx, <span style="color:#4C58FF">['''RK''']</span>, in Worten ''':''' <span style="color:#FF6000">»''Wenn mit ,modaler‘ Notwendigkeit'' <span style="color:#00B000">( □ )</span>, ''für alle'' <span style="color:#4C58FF">‚'''x'''‘</span> ''die Aussage'' <span style="color:#4C58FF">‚'''A'''‘</span> ''gilt, und daraus logisch folgt, dass mit ,modaler‘ Notwendigkeit'' <span style="color:#00B000">( □ )</span>, ''für alle'' <span style="color:#4C58FF">‚'''x'''‘</span> ''auch die Aussage'' <span style="color:#4C58FF">‚'''B'''‘</span> ''gilt, dann ist in einem Kalkül regulär'' <span style="color:#00B000">( ├ )</span> ''ableitbar'' ''':''' ''es ist wahr, dass mit ,modaler‘ Notwendigkeit'' <span style="color:#00B000">( □ )</span>, ''für alle'' <span style="color:#4C58FF">‚'''x'''‘</span> ''aus der Aussage'' <span style="color:#4C58FF">‚'''A'''‘</span> ''die Aussage'' <span style="color:#4C58FF">‚'''B'''‘</span> ''logisch folgt''«</span>. Der Term <span style="color:#4C58FF">— ‚'''□∀xXx'''‘ —</span> in der rechten Spalte der Zeile 11 und der Zeile 18, <span style="color:#00B000">(gleichlautend mit Term :10:)</span>, steht formal-syndaktisch, allgemein nur für ,Transzendentalia‘, und ist für alle ,Nicht-Transzendentalia‘ <span style="color:#FF6000">„modal falsch“</span>. Dasselbe gilt auch für das Prinzip <span style="color:#4C58FF">['''RK''']</span>.
====<div class="center"><span style="color:#660066">2. Beweisgang</span></div>====
{|class="wikitable"
|-
! <div class="center"><span style="color:#660066">GÖDELS ontologischer Beweis für Theorem 2, (,Basisbeweis‘)</span></div>
|-
! <span style="color:#00B000">''Terme der erweiterten Prädikatenlogik zweiter Stufe____________„Benennungen“ und durchgeführte Logik-Operationen''</span>
|-
|
<small>
<math>\begin{align}
\text{(Axiom 1.2)} & \quad \neg P\ X \;\Longrightarrow\;\ P\neg X\ & \ & \text{„Wenn X nicht positiv ist, dann ist die Negation ¬X positiv“} \\
\text{(Axiom 3)} & \quad \ P\ G \ & \ & \text{„Göttlichkeit, GOTT-Sein, ist eine pos. Eigenschaft“ ↔ „GOTT ist perfekt“} \\
\text{(Axiom 4)} & \quad \ P\ X \;\Longrightarrow\; \Box \ P\ X \ & \ & \text{„Positive Eigenschaften sind notwendig aus sich positiv“} \\
\text{(Definition 1)} &\quad \ G\ x \;\Longleftrightarrow\; \forall X(\ P \ X \Longrightarrow \ X \ x)\ & \ & \text{„x ist genau dann GOTT, wenn x alle positiven Eigenschaften hat“} \\
\text{(Definition 2)} & \quad \ X_{ess}\ x \;\Leftrightarrow X\ x \wedge \forall Y \left(\ Y\ x \Rightarrow \Box \; \forall y (\ X\ y \Rightarrow \ Y\ y)\right) & \ & \text{„X ist genau dann eine wesentliche Eigenschaft von x, wenn x sie hat, und} \\
\text{ } & \quad & \text { } & \;\;\text{alle anderen Eigenschaften Y von x notwendig aus dieser Eigenschaft X folgen“} \\
\text{[RM]} &\quad \ A \;\Longrightarrow\;\ B\; \text{ ├ }\;\Box \; A \Longrightarrow\;\Box\; \ B\ & \ & \text{( :: Modales Prinzip)} \\
\text{(Theorem 2)} &\quad \ G\ x \;\Longleftrightarrow\; \ G_{ess}\ x \ & \ & \text{(,G‘ :: „Göttlichkeit“ ↔ „GOTT“ ↔ „Dasein GOTTES“)} \\
\text{ } &\;\text{„Das Wesen GOTTES ist sein Dasein“} & \ & \Longleftarrow\; \text{was zu beweisen ist !} \\
\text{01} &\quad \ G\ x \ & \ & \text{ AE: „Angenommen, x steht für den GOTT der Christen“} \\
\text{02} &\quad \ Y\ x \ & \ & \text{ AE: „Angenommen, GOTT hat die Eigenschaften Y“} \\
\text{03} &\quad \neg P\ Y & \ & \text{ AE: „Angenommen, die Y in GOTT sind nicht positiv“} \\
\text{04} &\quad \neg P \ Y \Rightarrow \ P \neg Y\ & \ &\text{(A1.2):Instanz(X:=Y) :: (Substitution für Eigenschaften) } \\
\text{05} &\quad \ P \neg Y \ & \ & \text {:03:04:[Modus ponens] :: [A, A→B ├ B] } \\
\text{06} &\quad \forall X(\ P \ X \Rightarrow \ X \ x)\ & \ &\text{(D1):01:[Modus ponens] :: (logische Schlussregel)} \\
\text{07} &\quad \ P \ X \Rightarrow \ X \ x\ & \ &\text{:06:[ⱯXA(X) ├ A(X)] :: (Quantorenregel)} \\
\text{08} &\quad \ P \neg Y \Rightarrow \neg Y \ x\ & \ &\text{:07:Instanz(X:=¬Y)} \\
\text{09} &\quad \neg Y \ x\ & \ &\text{:05:08:[Modus ponens]} \\
\text{10} &\quad \text{ ├ }\; (Y\ x \wedge \neg Y \ x) \;\Leftrightarrow\;\ F\ & \ & \text{:02:09:[Konjunktion] ⇒ Kontradiktion !} \\
\text{11} &\quad \neg P\ Y \; \Rightarrow \; (Y\ x \wedge \neg Y \ x )\ & \ &\text{:03:10:[├A├B ╞ A→B] :: ''KONDITIONALER BEWEIS''} \\
\text{12} &\quad \neg\neg P\ Y \ & \ &\text{:11:10:[Modus tollendo tollens] :: [A→B,¬B ├ ¬A]} \\
\text{13} &\quad \text{ ├ }\; P\ Y \ & \ &\text{:12:NEG; bzw. :03:10:[├¬A→F ╞ A] :: ''INDIREKTER BEWEIS'' :AE:} \\
\text{14} &\quad \ P\ Y \;\Rightarrow\;\Box \ P\ Y \ & \ & \text{(A4):Instanz(X:=Y)} \\
\text{15} &\quad \Box \ P\ Y \ & \ & \text{:13:14:[Modus ponens]} \\
\text{16} &\quad \ G \ y \Rightarrow \ Y \ y\ & \ &\text{:01:02:[├A├B ╞ A→B]:FUB(x:=y)} \\
\text{17} &\quad \text{ ├ }\; \forall y(\ G \ y \Rightarrow \ Y \ y)\ & \ &\text{:16:[G(y) ├ ⱯyG(y)]} \\
\text{18} &\quad \Box \ P\ Y \;\Rightarrow\; \Box \; \forall y(\ G \ y \Rightarrow \ Y \ y)\ & \ &\text{:13:17:[├A├B ╞ A→B]:[RM]} \\
\text{19} &\quad \text{ ├ }\; \Box \; \forall y(\ G \ y \Rightarrow \ Y \ y)\ & \ &\text{:15:18:[Modus ponens]} \\
\text{20} &\quad \ Y\ x \;\Rightarrow\; \Box \; \forall y(\ G \ y \Rightarrow \ Y \ y)\ & \ &\text{:02:19:[├A├B ╞ A→B]} \\
\text{21} &\quad \ (Y\ x \;\Rightarrow\; \Box \; \forall y(\ G \ y \Rightarrow \ Y \ y)) \wedge \ G \ x & \ &\text{:20:01:[Konjunktion] :: [A,B ├ A∧B]} \\
\text{22} &\quad \ (Y\ x \;\Rightarrow\; \Box \; \forall y(\ X \ y \Rightarrow \ Y \ y)) \wedge \ X \ x\;\Leftrightarrow\; X_{ess}\ x \ & \ &\text{(D2):KOMM(↔):KOMM(∧):[ⱯYA(Y) ├ A(Y)] wegen :13:} \\
\text{23} &\quad \ (Y\ x \;\Rightarrow\; \Box \; \forall y(\ G \ y \Rightarrow \ Y \ y)) \wedge \ G \ x\;\Leftrightarrow\; G_{ess}\ x \ & \ &\text{:22:Instanz(X:=G)} \\
\text{24} & \quad \text{ ├ }\; G_{ess}\ x \ & \ &\text{:21:23:[Modus ponens]:AE: wegen :30:} \\
\text{25} &\quad \ G\ x \;\Rightarrow\; \ G_{ess}\ x \ & \ & \text{:01:24:[├A├B ╞ A→B] :: Theorem 2.1} \\
\text{26} &\quad \forall X(\ P \ X \Rightarrow \ X \ x)\ & \ &\text{(D1):01:[Modus ponens] } \\
\text{27} &\quad \ P \ X \Rightarrow \ X \ x\ & \ &\text{:26:[ⱯXA(X) ├ A(X)]} \\
\text{28} &\quad \ P \ G \Rightarrow \ G \ x\ & \ &\text{:27:Instanz(X:=G)} \\
\text{29} &\quad \text{ ├ }\; G \ x\ & \ &\text{(A3):28:[Modus ponens]} \\
\text{30} &\quad \ G_{ess}\ x \;\Rightarrow \ G \ x\ & \ &\text{:24:29:[├A├B ╞ A→B] :: Theorem 2.2 } \\
\text{31} &\quad \ G\ x \;\Longleftrightarrow\; \ G_{ess}\ x \ & \ & \text{:25:30:[Konjunktion]:BIKONDITIONAL :: [(A→B) ∧ (B→A) ↔ (A↔B)] } \\
\text{(Theorem 2)} &\; \text{„Dasein ist das Wesen GOTTES“} & \ & \Longleftarrow\; \text{was zu beweisen war ! } \\
\text{32} &\quad \text{ ├ }\; \Box \; \forall y(\ G \ y \Rightarrow \ (x = y))\ & \ & \text{:19:Instanz(Y:=(x=..))} \\
\text{33} &\quad \ G\ x \;\Rightarrow\; \Box \; \forall y(\ G \ y \Rightarrow \ (x = y))\ & \ & \text{:01:32:[├A├B ╞ A→B]} \\
\text{(Korollar 2)} & \;\text{„Es gibt notwendig höchstens einen GOTT“} & \ & \text{„Wenn es GOTT gibt, dann gibt es für jede mögliche Welt nur einen GOTT“} \\
\end{align}</math>
</small>
|}
<span style="color:#00B000"><small>(In den Kalkül-Zeilen 16, 18, 31 mussten zwei-, und in Zeile 22 drei Kalkül-Schritte, d.h. Logik-Operationen in eine Zeile zusammengezogen werden, weil der Parser dieser speziellen Mathematik-Funktion in Wikibooks jedes Mal wegen Puffer-Überlauf abstürzt, wenn zu den bestehenden Zeilen noch eine neue Zeile, oder ein Text-Element, zusätzlich eingefügt wird. Das vermindert etwas die Transparenz des Kalküls.)</small></span>
Anmerkung-2 ''':''' <span style="color:#00B000">(Dieser Beweisgang kommt auch ohne das ,unbestimmte‘ Konjunkt <span style="color:#4C58FF"> — ‚'''Xx'''‘ —</span> in der Definition-2 zum gleichen Ergebnis, und wird dadurch um eine Zeile verkürzt ''':''' Zeile 21 entfällt, und <span style="color:#4C58FF">[ KOMM(∧) ]</span> ist dann unnötig. Dieses Konjunkt wird hier ebenfalls schon in der Kalkül-Prämisse :01: <span style="color:#4C58FF"> — ‚'''Gx'''‘ —</span>, als ,Annahme‘ gesetzt, vorentschieden und ,bestimmt‘ mit der <span style="color:#4C58FF">[ Instanz(X:=G) ]</span>. Es war also logisch korrekt, dass GÖDEL, in seiner Notiz vom 10. Feb. 1970 zum ontologischen Beweis, dieses Konjunkt weggelassen hat, was ihm von Kommentatoren als ein Flüchtigkeitsfehler angerechnet worden war. Der gesamte 2. Beweisgang bewegt sich im Geltungsbereich der Prämisse Term :01:, d.h. ist in jeder Zeile von der Annahme abhängig ''':''' die Variable <span style="color:#4C58FF"> — ‚'''x'''‘ —</span> steht für den GOTT der Christen. In der Kalkül-Zeile 33 wird, mit Korollar-2, diese Abhängigkeit, für den Term :32:, explizit dargestellt.)</span>
Der Beweisgang geht mit der Prämisse :01: prinzipiell, als ,modal‘ freigewählte Voraussetzung, von der Existenz eines GOTTES aus. Im 1. Beweisgang wurde bewiesen, dass die von GÖDEL ,postulierten‘ <span style="color:#FF6000">»''allgemeinen positiven Eigenschaften, Vollkommenheiten, Perfektionen'', <span style="color:#00B000">[ die sog. ,Transzendentalien‘ ]</span> ''konsistent''«</span>, d.i. widerspruchsfrei sind. Hier, in diesem Beweisgang wird nun die Prämisse vom 1. Beweisgang, <span style="color:#4C58FF"> — ‚'''PX'''‘ —</span>, im Bezug auf GOTT hinterfragt ''':''' Gibt es in GOTT auch so Etwas, wie <span style="color:#FF6000">»''Vollkommenheit, Positives, Perfektes''«</span> '''?''' Die ,Annahme‘ jedoch, dass es <span style="color:#FF6000">»''in GOTT keine Vollkommenheit, nichts Positives, nichts Perfektes''«</span> <span style="color:#00B000">(keine Transzendentalien)</span> gibt, <span style="color:#00B000">(Prämisse Term :03:)</span>,<span style="color:#4C58FF"> — ‚'''¬PY'''‘ —</span>, d.h. dass die <span style="color:#00B000">(wesentlichen)</span> Eigenschaften in GOTT keine <span style="color:#FF6000">„Vollkommenheiten“</span> seien, führt aber zu einem unlösbaren Widerspruch, <span style="color:#00B000">(Term :10:)</span>. Mit Term :13:, als 1. Hauptergebnis, ist damit, — als ,neue‘ Prämisse, <span style="color:#00B000">(ersetzt Term :03:)</span> —, definitiv ,bewiesen‘ <span style="color:#00B000">( ╞ , d.h. es ist ,wahr‘ und ,widerspruchsfrei‘)</span>, dass alle Eigenschaften, die hier mit <span style="color:#4C58FF">— ‚'''Y'''‘ —</span> symbolisiert werden, <span style="color:#FF6000">„positive Eigenschaften“</span>, d.h. <span style="color:#FF6000">„Perfektionen“</span> sind, von denen das Kalkül ,annimmt‘, <span style="color:#00B000">(Prämissen Term :01:, Term :02: und speziell Term :16:)</span>, dass der GOTT der Christen sie besitzt. Alle ,Wesenseigenschaften‘ in GOTT ''':''' <span style="color:#4C58FF">— ‚'''Y'''<sub>ess</sub>‘ —</span>, die durch den Term :13:, <span style="color:#4C58FF">— ‚'''PY'''‘ —, </span> dargestellt werden, sind somit <span style="color:#FF6000">„Vollkommenheiten“</span> ''':''' <span style="color:#4C58FF">— ‚'''P'''‘ —</span><span style="color:#00B000">, (,ultimative Transzendentalien‘, aller ,Grade‘)</span>. Damit ist definitiv ‚bestätigt‘, <span style="color:#00B000">( ╞ , es ist ,wahr‘ und ,widerspruchsfrei‘)</span>, was mit Axiom-3 ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ —</span>, schon ‚angenommen‘ worden ist ''':''' <span style="color:#FF6000">»''GOTT ist perfekt; er hat alle positiven Eigenschaften''«</span>; und auch Definition-1 ist damit ,verifiziert‘ ''':''' <span style="color:#FF6000">»''GOTT ist genau deswegen GOTT, weil er, als GOTT, positive Eigenschaften aller Grade in sich schließt''«</span>; entsprechend dem Quelltext bei LEIBNIZ ''':''' <span style="color:#FF6000">»''GOTT ist ein Wesen von äußerster Größe und Vollkommenheit, das alle Grade derselben in sich schließt''«</span>. Immer vorausgesetzt, <span style="color:#00B000">(,angenommen‘)</span>, man glaubt an GOTT ''':''' <span style="color:#00B000">(Term :01:)</span>. <span style="color:#00B000">(Der ,Schlüsselbegriff‘ <span style="color:#4C58FF">— ‚'''P'''‘ —</span> ist der ,Schlüssel‘ zur Erkenntnis, dass GOTT ,notwendig‘, sowohl ,wesentlich‘ für uns, als auch an sich ,grundlos‘, immer schon ,da‘ ist.)</span> Hier, <span style="color:#00B000">(im 2. Beweisgang)</span>, hat Axiom-1, <span style="color:#00B000">(im Term :04:)</span>, sicher gestellt, dass die Eigenschaften in GOTT, <span style="color:#00B000">(Definition-1; Term :06:)</span>, tatsächlich <span style="color:#FF6000">„ultimativ positiv, perfekt und vollkommen“</span> sind ''':''' <span style="color:#4C58FF">— ‚'''PY'''‘ —</span>. Das GÖDEL-Axiom-1 bezieht seine ,Potenz‘ aus dem Prinzip vom ,auszuschließenden‘ Widerspruch ''':''' eine Eigenschaft kann nicht zugleich ,positiv‘ und ,nicht positiv‘ sein '''!'''
Formal lässt sich das 2. Hauptergebnis, Theorem-2 ''':''' <span style="color:#4C58FF">— ‚'''Gx'''↔'''G'''<sub>ess</sub>'''x'''‘ — </span> ''':''' schon aus Term :23: in diesem Beweisgang mit der <span style="color:#4C58FF">[ Vereinfachung ] :: [ A∧B ├ B ]</span> ohne Weiteres ,regulär‘ erschließen, — analog zu den Vorgehensweisen bei A. FUHRMANN und G.J. WIRSCHING. <span style="color:#00B000">(Beide Aussagen dieser ,Konjunktion‘ sind ,gleichwertig‘ und wahr, daher partizipiert das Theorem-2 auch am Ergebnis der Widerspruchsfreiheit von Term :13:, <span style="color:#4C58FF">— ‚'''PY'''‘ —</span>, dem 1. Hauptergebnis. Beide, mit ,und‘, <span style="color:#4C58FF">— ‚'''∧'''‘ —</span>, verbundene, Aussagen im Term :23: interpretieren sich gegenseitig.)</span> Der hier gewählte, etwas längere Weg zum Ergebnis, soll die innere Struktur und Abhängigkeit der Ergebnisse von bestimmten Voraussetzungen offen legen, und ihren ,Zweck‘ verdeutlichen. Die beiden Hauptergebnisse im Basisbeweis gehen vom vorgefundenen und traditionell vorgegebenen Begriff von ,GOTT‘ aus, <span style="color:#00B000">(Term :06:, Term :16: und Term :26:)</span>. Das ,bewiesene‘ <span style="color:#00B000">( ╞ )</span> 1. Hauptergebnis, hier im 2. Beweisgang, Term :13: <span style="color:#4C58FF">— ‚'''PY'''‘ —</span> ''':''' <span style="color:#FF6000">»''alle Eigenschaften in GOTT sind vollkommen, d.h. sind die ultimativen Transzendentalia''«</span>, rechtfertigt, bzw. verifiziert sowohl Axiom-3 ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ —</span>, als auch die Definition-1 ''':''' <span style="color:#4C58FF">— ‚'''Gx↔∀X(PX→Xx)'''‘ —</span>, für die Annahme ''':''' den ,GOTT der Christen‘, der deswegen GOTT ist, weil er alle Grade der Vollkommenheit in sich schließt. Und das ebenfalls ,bewiesene‘ <span style="color:#00B000">( ╞ )</span> 2. Hauptergebnis, hier im selben Beweisgang, Theorem-2 ''':''' <span style="color:#4C58FF">— ‚'''Gx'''↔'''G'''<sub>ess</sub>'''x'''‘ —</span> ''':''' <span style="color:#FF6000">»''das Wesen GOTTES ist sein eigenes Sein''«</span>, rechtfertigt, bzw. verifiziert sowohl Axiom-5 ''':''' <span style="color:#4C58FF"> — ‚'''PE'''<sub>not</sub>‘ —</span>, als auch die Definition-3 ''':''' <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>'''x ↔ ∀X(X'''<sub>ess</sub>'''x →□∃yXy)'''‘ —</span>, für die Wesenseigenschaft ''':''' ,notwendige Existenz‘, und widerlegt den Einwand KANTS, für den Spezialfall ''':''' <span style="color:#4C58FF">— ‚'''x'''‘ —</span> steht für den ,GOTT‘ der Christen. Zwei Axiome und zwei Definitionen von GOTT und seinen Vollkommenheiten werden durch die Ergebnisse im Basisbeweis des GÖDEL-Kalküls in unserer realen Welt als ,wahr‘, <span style="color:#00B000">(genauer als ,widerspruchsfrei‘)</span>, und, — im Rahmen des christlichen Glaubens —, als ,annehmbar‘ bestätigt. <span style="color:#00B000">(Anmerkung zu Term :24: ''':''' eine Prämisse ist regulär-,modal‘ immer ,frei‘ wählbar.)</span>
Zusammengefasst heißt das ''':''' die ,strittige‘ Begründung der ,methodologischen‘ Prämisse des GÖDEL-Kalküls ''':''' <span style="color:#4C58FF">— ‚'''Gx'''‘ —</span> ''':''' <span style="color:#00B000">(Prämisse, Term :01:)</span>, weil <span style="color:#4C58FF">— ‚'''◇∃xGx'''‘ —</span> ''':''' <span style="color:#00B000">(Korollar-1)</span> ''':''' <span style="color:#FF6000">»''Das <span style="color:#4C58FF">‚'''x'''‘</span> steht für den GOTT der Christen, für den es ohne Widerspruch denkbar ist, dass es ihn gibt''«</span>, <span style="color:#00B000">(ANSELMS Prinzip, trotz der ,Warnung‘ KANTS)</span>, ist ,wahr‘ und für uns ,annehmbar, denn es ist auch, auf Grund der Ergebnisse des 2. Beweisganges, in unserer realen Welt ,wahr‘ und ,annehmbar‘, weil schon als ,widerspruchsfrei‘ verifiziert ''':''' <span style="color:#FF6000">»''Der GOTT der Christen'' <span style="color:#00B000">(Term :01:)</span> '',existiert‘ für uns ,notwendig‘''«</span> ''':''' <span style="color:#4C58FF"> — ‚'''Gx→E'''<sub>not</sub>'''x'''‘ —</span>, <span style="color:#00B000">(d.i. das ,regulär‘-mögliche Korollar sowohl im 2. als auch im 3. Beweisgang)</span>, denn dieser GOTT ist aus sich ,vollkommen‘ ''':''' <span style="color:#4C58FF"> — ‚'''PG'''‘ —</span>, und zu seiner ,Vollkommenheit‘ <span style="color:#4C58FF">— ‚'''P'''‘ —</span> gehört auch notwendig sein ,Existieren‘ ''':''' <span style="color:#4C58FF"> — ‚'''PE'''<sub>not</sub>‘ —</span>. <span style="color:#00B000">(Jeder dieser Terme ist im-, und durch den 2. Beweisgang, im Geltungsbereich der Prämisse Term :01:, als ,wahr‘ und ,annehmbar‘ bewiesen, bzw. verifiziert.)</span> Das ist der ,Kern‘ des ontologischen Arguments, und somit ist auch diese ,strittige‘ Begründung der Prämisse des GÖDEL-Kalküls mit den Maßstäben der modernen Logik <span style="color:#FF6000">»''durchaus vereinbar''«</span>, d.h. sie ist logisch ,richtig‘ und, im Kontext des christlichen Glaubens, vernünftig. Die Annahme des Gegenteils zu dieser Prämisse ''':''' <span style="color:#4C58FF">— ‚'''¬◇∃xGx'''‘ —</span> ''':''' <span style="color:#FF6000">»''Es ist undenkbar, dass es diesen GOTT gibt''«</span>, führt jedoch, unabhängig von jeder Glaubensüberzeugung, zu einem Widerspruch — ist unlogisch und daher ,falsch‘, <span style="color:#00B000">(siehe Anhang ''':''' Widerlegung)</span>. Die Behauptung einer ,formalen Unentscheidbarkeit‘ zu den Annahmen über die Existenz GOTTES, ob oder nicht, <span style="color:#00B000">(d.h. ein ,methodologischer‘ Agnostizismus)</span>, ist gegen jede ,Logik‘; und ist auch ,falsch‘, d.h. der Agnostizismus ist einfach nur ein ,logischer Irrtum‘. Denn aus dem, im Kalkül abgeleiteten, Widerspruch aus der einen Annahme, und damit ihrer Unrichtigkeit, folgt notwendig die Richtigkeit der gegenteiligen Annahme. Damit ist eine klare Entscheidung getroffen.
Das Modale-Prinzip <span style="color:#4C58FF">['''RM''']</span> in Worten ''':''' <span style="color:#FF6000">»''Wenn es wahr ist, dass aus'' <span style="color:#4C58FF"> — ‚'''A'''‘ —</span> ''ein'' <span style="color:#4C58FF"> — ‚'''B'''‘ —</span> ''logisch folgt, dann ist in einem Kalkül regulär'' <span style="color:#00B000">( ├ )</span> ''ableitbar, dass, wenn'' <span style="color:#4C58FF"> — ‚'''A'''‘ —</span> '',modal‘ notwendig'' <span style="color:#00B000">( □ )</span> ''ist, auch'' <span style="color:#4C58FF"> — ‚'''B'''‘ —</span> '',modal‘ notwendig'' <span style="color:#00B000">( □ )</span> ''ist''«</span>. <span style="color:#00B000">(Das <span style="color:#4C58FF"> — ‚'''A'''‘ —</span> in Term :18: ist als ,modal notwendig‘ durch den Term :15: verifiziert '''!''' )</span>
Mit dem 2. Hauptergebnis, Theorem-2 ''':''' <span style="color:#FF6000">»'',Dasein‘ ist das ,Wesen‘ GOTTES''«</span>, folgt die GÖDEL-Axiomatik der philosophisch-<span style="color:#4C58FF">,theologischen‘</span> Tradition der ,Rede von GOTT‘ seit ARISTOTELES, und schließt sich damit formal-syntaktisch zugleich auch der religiösen Überzeugung der Christen an, die glauben, dass GOTT, als unser Vater, aus Liebe, in seinem Sohn, JESUS CHRISTUS, für uns immer schon <span style="color:#FF6000">»''da''«</span> ist, <span style="color:#00B000">(der Sohn ist koinzident ,eins‘ mit GOTT, dem Vater und dem GEIST)</span>, wirksam in und durch seine <span style="color:#4C58F0">„Kirche“</span>, im HEILIGEN GEIST, bis ans Ende der Zeit. Das ist das, <span style="color:#FF6000">»''was''«</span> GOTT eigentlich für uns ausmacht, — die Selbstmitteilung seines unergründlichen Wesens in den Sakramenten der <span style="color:#4C58F0">„Kirche“</span> ''':''' <span style="font-family: Times;"><big>‘אֶֽהְיֶ֖ה אֲשֶׁ֣ר אֶֽהְיֶ֑ה‚</big></span> <span style="color:#00B000">| ‚eh'jeh asher eh'jeh‘ |</span> <span style="color:#CC66FF">»''Ich bin da für euch und für immer, als der ich ''<span style="color:#00B000">[ immer schon gewesen ]</span> ''bin''«</span>; <span style="color:#00B000">(d.i. das <span style="color:#4C58FF">,theologisch‘</span>-exegetische ,Axiom‘ der Christen, und eine <span style="color:#4C58FF">,theologisch‘</span> korrekte Explikation der ,regulären‘ Kalkül-Prämisse Term :01: <span style="color:#4C58FF"> — ‚'''Gx'''‘ —</span>, jeweils im 2. und 3. Beweisgang)</span>. Das heißt aber nicht, dass der Autor des Kalküls sich mit diesem Glauben identifiziert hat, <span style="color:#00B000">(,hat‘ er auch nicht)</span>, oder dass der Leser des ontologischen Beweises von Kurt GÖDEL sich damit identifizieren muss, wenn er dessen <span style="color:#FF6000">»''mathematische Evidenz''«</span> anerkennt.
Zur erweiterten <span style="color:#4C58FF">„theologischen“</span> Explikation der Kalkül-Prämisse ''':''' Die <span style="color:#4C58F0">„Kirche“</span> ist das ,Meisterwerk‘ GOTTES ''':''' In ihr ist es GOTT gelungen, etwas Göttliches und Unzerstörbares in unsere korrupte Welt einzupflanzen ''':''' <span style="color:#4C58FF"> — ‚'''Gx→E'''<sub>not</sub>'''x'''‘ —</span> ''':''' <span style="color:#FF6000">»''Etwas Göttliches existiert notwendig, d.h. ,unzerstörbar‘ in unserer Welt''«</span>. Sie ist, durch die Menschwerdung des GOTTES Sohnes, JESUS CHRISTUS, dessen <span style="color:#4C58F0">„Leib“</span> die <span style="color:#4C58F0">„Kirche“</span> ist, untrennbar mit Menschen verbunden, die schon, von allem Anfang an, und jetzt immer noch, durch die Sünde korrumpiert sind. Mit ihr will und wird GOTT unsere Welt und die Menschheit, bis ans Ende der Zeit, von der Sünde und von deren Konsequenz, dem <span style="color:#00B000">(ewigen)</span> Tod <span style="color:#4C58FF">„erlösen“</span>, <span style="color:#00B000">(jedoch nicht ohne die Zustimmung des Menschen)</span>. Mit dieser Explikation wird die Tragweite des ontologischen Arguments ANSELMS, und damit auch die <span style="color:#4C58FF">„theologische“</span> Relevanz der GÖDEL-Axiomatik erkennbar. Immer vorausgesetzt, <span style="color:#00B000">(,angenommen‘)</span>, man glaubt an GOTT, <span style="color:#00B000">(Term :01:)</span>.
====<div class="center"><span style="color:#660066">3. Beweisgang</span></div>====
{|class="wikitable"
|-
! <div class="center"><span style="color:#660066">GÖDELS ontologischer Beweis für Theorem 3, (ANSELMS Theorem)</span></div>
|-
! <span style="color:#00B000">''Terme der erweiterten Prädikatenlogik zweiter Stufe___________________„Benennungen“ und durchgeführte Logik-Operationen''</span>
|-
|
<small>
<math>\begin{align}
\text{(Axiom 5)} & \quad P\ E_{not}\; \ & \text { } & \text{„Notwendige Existenz ist eine positive Eigenschaft“} \\
\text{ } & \text{( :: Das ist nur dann wahr, wenn ,Dasein‘ und ,Wesen‘ } & \ & \text{( :: dagegen KANT : ,Existenz‘ ist keine ,Eigenschaft‘,} \\
\text{ } & \;\;\text{in eins zusammenfallen ! ARISTOTELES : Theorem-2)}\ & \ & \;\;\text{,Sein‘ ist für alles, was existiert, kein ,reales Prädikat‘ ! )} \\
\text{(Definition 1)} &\quad \ G\ x \;\Longleftrightarrow\; \forall X(\ P \ X \Longrightarrow \ X \ x)\ & \ & \text{„x ist genau dann GOTT, wenn x alle positiven Eigenschaften hat“} \\
\text{(Definition 3)} & \quad \ E_{not}\ x \;\Longleftrightarrow\;\ \forall X \left(\ X_{ess}\ x \Longrightarrow \Box \; \exists y \ X\ y \right) & \ & \text{„Notwendige Existenz ist genau dann eine Eigenschaft von x, wenn} \\
\text{ } & \quad & \ & \;\;\text{alle wesentl. Eigenschaften von x notwendig instanziiert sind“} \\
\text{(Korollar 1)} & \quad \Diamond \; \exists x \ G \ x \ & \ & \text{„Es ist widespruchsfrei möglich, dass es GOTT gibt“} \\
\text{(Theorem 2)} &\quad \ G\ x \;\Longleftrightarrow\; \ G_{ess}\ x \ & \ &\text{„Dasein, GOTT-Sein, Existenz ist das Wesen, die Essenz GOTTES“} \\
\text{(Korollar 2)} &\quad \ G\ x \;\Longrightarrow\; \Box \; \forall y(\ G \ y \Longrightarrow \ (x = y))\ & \ &\text{„Wenn es GOTT gibt, dann gibt es notwendig nur einen GOTT“} \\
\text{(Theorem 3)} & \quad \Diamond \; \exists x \ G \ x \;\Longrightarrow\; \Box \; \exists x \ G \ x \ & \ & \text{( :: ANSELMS Prinzip)} \\
\text{ } & \text{„Weil es widerspruchsfrei möglich ist, dass es GOTT gibt,} & \ & \Longleftarrow\; \text{was zu beweisen ist !} \\
\text{ } & \;\;\text{ist der Glaube, dass es GOTT wirklich gibt, widerspruchsfrei“} \\
\text{01} & \quad \ G \ x\ & \ & \text{ AE: „Angenommen, x steht für den GOTT der Christen“} \\
\text{02} & \quad \forall X(\ P \ X \Rightarrow \ X \ x)\ & \ & \text{(D1):01:[Modus ponens] :: (logische Schlussregel)} \\
\text{03} & \quad \ P \ X \Rightarrow \ X \ x\ & \ & \text{:02:[ⱯXA(X) ├ A(X)] :: (Quantorenregel)} \\
\text{04} & \quad \ P \ E_{not}\;\Rightarrow \ E_{not}\ x\ & \ & \text{:03:Instanz(X:= Enot) :: (Substitution für Eigenschaften)} \\
\text{05} & \quad \ E_{not}\ x\ & \ & \text{(A5):04:[Modus ponens] :: [A, A→B ├ B]} \\
\text{06} & \quad \forall X \left(\ X_{ess}\ x \Rightarrow \Box \; \exists y \ X\ y \right) & \ & \text{(D3):05:[Modus ponens] :: [A→B, A ├ B]} \\
\text{07} & \quad \ X_{ess}\ x \Rightarrow \Box \; \exists y \ X\ y & \ & \text{:06:[ⱯXA(X) ├ A(X)]} \\
\text{08} & \quad \ G_{ess}\ x \Rightarrow \Box \; \exists y \ G\ y & \ & \text{:07:Instanz(X:=G)} \\
\text{09} & \quad \ G_{ess}\ x \ & \ & \text{(Th2):01:[Modus ponens]} \\
\text{10} & \quad \text{ ├ }\;\Box \; \exists x \ G\ x & \ & \text{:08:09:[Modus ponens]:FUB(y:=x) :: (Freie-Um-Benennung der Var.)} \\
\text{ } & \text{„Es gibt GOTT wirklich, für jede mögliche Welt“} & \ & \Longleftarrow\; \text{1. Hauptergebnis !} \\
\text{11} & \quad \;\Diamond \exists x \ G \ x \;\Longrightarrow \; \Box \; \exists x \ G\ x & \ & \text{(K1):10:[├A├B ╞ A→B] :: ''KONDITIONALER BEWEIS''} \\
\text{(Theorem 3)} & \;\text{„Weil es widerspruchsfrei möglich ist, dass es GOTT gibt,} & \ & \Longleftarrow\; \text{was zu beweisen war ! 2. Hauptergebnis ! } \\
\text{ } & \;\;\text{ist der Glaube, dass es GOTT wirklich gibt, widerspruchsfrei“} \\
\text{12} & \quad \;\Box \; \forall y(\ G \ y \Longrightarrow \ (x = y))\ & \ &\text{(K2):01:[Modus ponens]} \\
\text{13} & \quad \;\Box \; (\exists x \ G\ x \wedge \; \forall y(\ G \ y \Longrightarrow \ (x = y)))\ & \ & \text{:10:12:[Konjunktion]:DIST(□∧)} \\
\text{(Korollar 3)} & \;\text{„Es gibt notwendig genau nur einen GOTT“} & \ & \text{„Es gibt für jede mögliche Welt nur den GOTT der Christen“} \\
\end{align}</math>
</small>
|}
Anmerkung-3 ''':''' <span style="color:#00B000">(Ein Theorem und zwei Korollare, aus den beiden vorhergehenden Beweisgängen, werden hier, im 3. Beweisgang, zu verifizierten ,Axiomen‘, die das Theorem-ANSELMS und sein Korollar mit-verifizieren und bestätigen.)</span>
Dieser Beweisgang ist das Ziel aller Bemühungen. Hier wird der sog. ,ontologische Gottesbeweis‘ nach ANSELM von Canterbury formal-syntaktisch dargestellt und als logisch nachvollziehbar von GÖDEL bestätigt. Damit hat er aber auch klar gestellt, dass der ontologische Beweis ANSELMS kein Beweis für die ,Existenz‘ des GOTTES der Bibel sein kann, bzw. sein ,will‘ ''':''' Denn mit der Prämisse, <span style="color:#4C58FF">— ‚'''Gx'''‘ —</span>, <span style="color:#00B000">(Term :01:, wie auch schon im ,Basisbeweis‘, und ausformuliert hier in Term :02:, mit der Definition für GOTT)</span>, wird mit dem traditionellen, abendländischen ,GOTT-Glauben‘, der ,glaubt‘, dass der Gott der Christen tatsächlich existiert, — methodologisch als ,Annahme‘ —, der Beweisgang schon regulär und explizit eröffnet, aus dem sich dann, logisch korrekt, mit Hilfe der GÖDEL-Axiome und Definitionen, das ,Theorem ANSELMS‘ ergibt; <span style="color:#00B000">(hier jedoch, mit Günther J. WIRSCHING, ohne den Umweg bei GÖDEL über den <span style="color:#4C58FF">[ Modus Barbara ] :: [ A → B, B → C ├ A → C ] </span>, mit dem modalen Axiom-BECKER ''':''' <span style="color:#4C58FF">— ‚'''◇□A→□A'''‘ —</span>, als <span style="color:#4C58FF">‚B → C‘</span> im kategorischen Syllogismus, das André FUHRMANN recherchiert hat)</span>. GÖDEL verwendet zur Darstellung des sog. ,ontologischen Gottesbeweises‘, nach ANSELM, die Struktur eines modal-logischen Kalküls. Ein modal-logisches Kalkül ist ein genau geregeltes Schema, in dem bei bestimmten ,Annahmen‘ <span style="color:#00B000">(Axiome, Definitionen, Prämissen)</span> etwas anderes als das Vorausgesetzte, auf Grund des Vorausgesetzten mit Notwendigkeit folgt. Entsprechend der ,Modalität‘ der sechs ,modal‘ notwendigen, d.h. transzendentalen Voraussetzungen, hier, für den 3. Beweisgang, die in den <span style="color:#00B000">(und durch die)</span> beiden vorhergehenden Beweisgängen schon als <span style="color:#FF6000">„modal wahr“</span>, bzw. als logisch annehmbar, verifiziert und/oder ,bewiesen‘ wurden, sind auch die beiden ,Schlusssätze‘ <span style="color:#00B000">(Theorem-3 und Korollar-3)</span> <span style="color:#FF6000">„modal wahr“</span>, bzw. logisch annehmbar '''!''' Die Wahl der Prämisse :01: dagegen ist nicht ,modal‘ notwendig, sondern beruht auf einer freien Entscheidung, und damit ist auch ihre Interpretation eine ,modal‘ freie Entscheidung, mit der Voraussetzung, dass man das Kalkül mit Theoremen aus der <span style="color:#4C58FF">„christlichen Theologie“</span> evaluieren, und damit interpretieren will. Dazu berechtigt die Genese des Kalküls. Der Glaube an den GOTT der Christen ''':''' <span style="color:#4C58FF">— ‚'''Gx'''‘ —</span>, beruht immer auf einer freien Entscheidung. Das Kalkül, als solches, unabhängig von jeder Interpretation seiner Syntax, ist genau dann ,allgemein‘ <span style="color:#FF6000">»''mathematisch evident''«</span>, d.h. ,ist allgemein gültig‘, wenn es gültigen Logik-Regeln folgt. Die Bestimmung seiner Syntax jedoch, d.h. seine Interpretation, unterliegt hermeneutischen Kriterien, die nicht von Logik-Regeln abhängen, wie hier ''':''' <span style="color:#FF6000">»''(unabhängig von der zufälligen Struktur der Welt). Nur dann sind die Axiome wahr''«</span>, wie GÖDEL selbst hinzufügt. Mit der, — von GÖDEL eingeforderten —, ‚Unabhängigkeit‘ der Kalkül-Axiome von der zufälligen Struktur der Welt, wird implizit für das Kalkül auch festgelegt, dass <span style="color:#FF6000">„GOTT“</span> ‚unabhängig‘ von der zufälligen <span style="color:#00B000">(Raum-Zeit-)</span>Struktur unserer vergänglichen Welt, und daher ,zeitlos-ewig‘ ist, <span style="color:#00B000">(was <span style="color:#4C58FF">„theologisch“</span> korrekt ist)</span>, begründet durch Definition-1 und Axiom-3. Aus der zeitlosen Ewigkeit GOTTES folgt, dass GOTT, <span style="color:#FF6000">„unverursacht“ <span style="color:#00B000">|</span> „grundlos“</span>, für uns immer schon ‚da‘ ist, denn bei Zeitlosigkeit gibt es keinen ,zeitlichen‘ und damit auch keinen ,ontologischen‘ Unterschied zwischen ‚Ursache‘ und ‚Wirkung‘. Beides ist dann koinzident ,eins‘ ''':''' wie ,Wesen‘ und ,Dasein‘ in GOTT, bzw. wie ,Begriff‘ und ,Sein‘, oder ,Möglichkeit‘ und ,Wirklichkeit‘. <span style="color:#00B000">(Man vergleiche damit auch die ,postulierte‘ Einheit von ,Erkenntnisobjekt‘ und ,Erkenntnissubjekt‘ im ,Gott‘ des ARISTOTELES ''':''' im <span style="color:#FF6000">»<span style="color:#00B000">[ selbstbewussten ]</span> ''Erkennen seiner Erkenntnis''<span style="color:#00B000">[-Tätigkeit ]</span>«<span style="color:#00B000"> | </span>„<span style="font-family: Times;"><big>νοήσεως νόησις</big></span>“<span style="color:#00B000"> | </span>„noêseôs noêsis“</span> <small>(‚<span style="font-family: Times;"><big>''Metaphysik''</big></span>‘ XII 9, 1074b34)</small>, im Vollzug seiner Funktion als ,unbewegtes Bewegungsprinzip‘, als <span style="color:#FF6000">„<span style="font-family: Times;"><big>πρῶτον κινοῦν ἀκίνητον</big></span>“<span style="color:#00B000"> | </span>„prôton kinoûn akinêton“</span> der Welt, das alles Übrige <span style="color:#FF6000">»''wie ein Geliebtes''«<span style="color:#00B000"> | <span style="color:#FF6000">„<span style="font-family: Times;"><big>ὡς ἐρώμενον</big></span>“</span> | <span style="color:#FF6000">„hôs erômenon“</span> bewegt; d.h. christlich ''':''' <span style="color:#FF6000">»''aus Liebe''«</span> ,entstehen‘ lässt.)</span>
Anmerkung-4 ''':''' Das <span style="color:#00B000">(im 2. Beweisgang)</span> schon bewiesene Theorem-2, d.i. die Koinzidenz von <span style="color:#FF6000">„Sein“</span> und <span style="color:#FF6000">„Wesen“</span> in GOTT, <span style="color:#00B000">(‚Existenz‘</span> und <span style="color:#00B000">‚Essenz‘)</span>, rechtfertigt sowohl Axiom-5 als auch die Definition-3, und widerlegt den Einwand KANTS. Somit ist deren Setzung <span style="color:#00B000">(hier, im 3. Beweisgang)</span> korrekt, und durch das Theorem-2 schon vorbestimmt und bestätigt, d.h. beide sind <span style="color:#FF6000">„modal wahr“</span> und ,logisch‘ annehmbar, da sie durch die Gültigkeit von Theorem-2 ,verifiziert‘ worden sind. Damit wird klar erkennbar, dass das Theorem-2 tatsächlich die Basis des GÖDEL-Kalküls ist. Und wenn damit Axiom-5 im GÖDEL-Kalkül ‚gerechtfertigt‘ ist, dann ist auch, <span style="color:#00B000">(als Voraussetzung dafür)</span>, das Axiom-4 ''':''' <span style="color:#4C58FF">— ‚'''PX → □PX'''‘ — ''':''' </span> <span style="color:#FF6000">»''Positive Eigenschaften'', <span style="color:#00B000">[ ,Transzendentalia‘ ]</span>, ''sind notwendig aus sich'', <span style="color:#00B000">[ von Natur aus ]</span>, ''positiv''«</span>, im 2. Beweisgang erklärbar, in dem die ‚Positivität‘, <span style="color:#4C58FF">— ‚'''P'''‘ —</span>, einer Eigenschaft schon als ‚notwendig‘, <span style="color:#4C58FF">— '''□''' —</span>, charakterisiert worden ist, äquivalent zu Axiom-5 ''':''' <span style="color:#4C58FF"> — ‚'''PE'''<sub>not</sub>‘ —</span>, in dem die ‚Notwendigkeit‘, <span style="color:#4C58FF">— <sub>not</sub> —</span>, <span style="color:#00B000">(der Existenz <span style="color:#4C58FF">— ‚'''E'''‘ —</span>)</span>, dann als ‚positive‘ Eigenschaft, <span style="color:#4C58FF">— ‚'''P'''‘ —</span>, ‚bestimmt‘ wird; <span style="color:#00B000">(unter der speziellen Voraussetzung, dass <span style="color:#FF6000">„Existieren“</span> definitiv als eine <span style="color:#FF6000">„Wesenseigenschaft“</span> in GOTT ,instanziiert‘ ist; vgl. Definition-3. Eine ,bestimmte‘ Eigenschaft ist genau dann ,instanziiert‘, wenn sie an einem Träger real ,existiert‘. Definition-3 ''':''' <span style="color:#4C58FF">— ‚'''E'''<sub>not</sub>'''x ↔ ∀X(X'''<sub>ess</sub>'''x →□∃yXy)'''‘ —</span>, besagt, dass die, von GÖDEL ,postulierte‘, und im Basis-Theorem-2 verifizierte, <span style="color:#FF6000">„notwendige Existenz“</span> zu den ,ultimativen‘ Transzendentalia in GOTT gehört; genauer ''':''' Sie ist die ,Summe‘ aller Transzendentalia.)</span>
Zum Axiom-4, <span style="color:#00B000">(bzw. zum Term :14:, im 2. Beweisgang)</span>, erklärt GÖDEL in seinen Notizen zum Kalkül ''':''' <span style="color:#FF6000">»''da es'' <span style="color:#00B000">[ das Notwendigsein, <span style="color:#4C58FF">— '''□''' —</span> ]</span> ''aus der Natur der'' <span style="color:#00B000">[ positiven ]</span> ''Eigenschaft folgt'', <span style="color:#00B000">[ deren Positivität, im selben Beweisgang, mit Term :13: vorher schon ,bewiesen‘ (╞ ) worden ist ]</span>«</span>. Diese GÖDEL-Erklärung zum Axiom-4 entspricht der Modal-Regel zur sog. ,Nezessisierung‘ in der KRIPKE-Logik, die dort GÖDEL zugeschrieben wird. <span style="color:#4C58FF">[ Nezessisierungsregel ] '''::''' [ ├ A →├ □ A ]</span> ''':''' <span style="color:#FF6000">»''Angenommen'', <span style="color:#4C58FF">‚'''A'''‘</span> ''ist in einem Kalkül eine Prämisse, oder darin regulär'' <span style="color:#4C58FF">(├ )</span> ''abgeleitet, dann folgt in diesem Kalkül regulär'' <span style="color:#4C58FF">(├ )</span>, ''dass dieses'' <span style="color:#4C58FF">‚'''A'''‘</span> ''auch ,modal‘ notwendig'' <span style="color:#4C58FF">— ,□‘ —</span> ''ist''«</span>. GÖDEL hat jedoch diese modale Notwendigkeit nur in der ,Natur‘ der positiven Eigenschaften, alias ,Transzendentalia‘, gesehen, und mit den Aussagen im Axiom-4 expliziert ''':''' <span style="color:#4C58FF">— ‚'''PX → □PX'''‘ —</span>, unter der unumgänglichen Bedingung ''':''' <span style="color:#FF6000">»''unabhängig von der zufälligen Struktur der Welt, nur dann sind die Axiome wahr''«</span>. Ob dieselbe modale ,Notwendigkeit‘ auch auf Aussagen über Eigenschaften, wie ,rot‘ oder ,schwer‘, die weder positiv noch negativ sind, zutrifft, ist nicht erwiesen. Hier kann nur von modaler ,Kontingenz‘ gesprochen werden, die universal, alles Seiende innerhalb der <span style="color:#FF6000">»''zufälligen Struktur der Welt''«</span> betrifft. Somit sind die Aussagen <span style="color:#4C58FF">— ‚'''A'''‘ —</span> in der GÖDEL-Regel, nur auf Aussagen über ,positive‘ Eigenschaften eingeschränkt, und nicht allgemein gültig. Die <span style="color:#4C58FF">[ Nezessisierungsregel ]</span> ist nur für <span style="color:#FF6000">„Transzendentalia“ „modal wahr“</span>, und für ,Nicht-Transzendentalia‘ immer <span style="color:#FF6000">„modal falsch“</span>.
Der unbegrenzt Unendliche, GOTT, — im Glauben der Christen —, ist deswegen ,notwendig für uns da‘ ''':''' <span style="color:#4C58FF">— ‚'''Gx→E'''<sub>not</sub>'''x'''‘ —</span>, weil er als GOTT ,vollkommen‘, ,perfekt‘ und absolut ,positiv‘, d.h. absolut ,gut allein‘ ist, ohne jede Negativität ''':''' <span style="color:#4C58FF">— ‚'''PG'''‘ —</span>; <span style="color:#00B000">(was auch schon im 2. Beweisgang mit Term :13: verifiziert wurde)</span>. Und wenn GOTT ,vollkommen‘, ,perfekt‘, ,positiv‘, und absolut ,gut‘ ist, dann ist er das auch ,notwendig aus sich‘ ''':''' <span style="color:#4C58FF">— ‚'''PG → □PG'''‘ — ::</span> <span style="color:#00B000">(als Zusatz-Korollar im 2. Beweisgang mit Axiom-4 und der <span style="color:#4C58FF">[ Instanz(X:=G) ]</span>)</span>, d.h. ,aus seinem Wesen‘. Das ist gerade das, ,was‘ GOTT als GOTT ausmacht ''':''' sein ,Wesen‘, bzw. seine <span style="color:#FF6000">„Natur“</span>. Zusammen mit der Definition-1 für GOTT, <span style="color:#00B000">(und der Definition-2 ''':''' Alle Wesenseigenschaften hängen notwendig gleichwertig aus sich zusammen)</span>, ist dieses, aus der <span style="color:#FF6000">„Natur“</span> GOTTES sich ergebende, ‚Notwendigsein‘, <span style="color:#4C58FF">— '''□''' —</span>, aller ‚positiven‘ Eigenschaften im Axiom-4, und ihr logischer Zusammenhang, d.i. die Koinzidenz aller ,Vollkommenheiten‘ und ,Zuschreibungen‘ im Unendlichen, GOTT, ihr ,Zusammenfallen in eins‘, die entscheidende Voraussetzung, aus der sich dann für GÖDEL <span style="color:#00B000">(im 2. Beweisgang)</span> auch der logische Zusammenhang, bzw. die ontologische Identität, <span style="color:#00B000">(die Koinzidenz)</span>, von <span style="color:#FF6000">„Dasein“</span> und <span style="color:#FF6000">„Wesen“</span> in GOTT, im Basis-Theorem-2 ''':''' <span style="color:#4C58FF"> — ‚'''Gx↔G'''<sub>ess</sub>'''x'''‘ —</span> mit Notwendigkeit ergibt. Dieses Theorem-2 ist dann, in weiterer Folge, die ,modal‘ notwendige, d.h. die transzendentale Voraussetzung auch für den Konsequenz-Teil im Theorem ANSELMS, <span style="color:#00B000">(Term :09: hier im 3. Beweisgang)</span>. <span style="color:#FF6000">„Positive Eigenschaften“<span style="color:#00B000"> | </span>„Vollkommenheiten“</span> sind ,immer‘ auch <span style="color:#FF6000">„notwendige Eigenschaften“</span>, daher ''':''' <span style="color:#4C58FF"> — ‚'''PE'''<sub>not</sub>‘ —</span>. Das ,Dasein‘, die <span style="color:#FF6000">„Existenz“</span> ist ,immer‘ etwas <span style="color:#FF6000">„Positives“</span>, speziell in GOTT, dem Schöpfer jeder ,Existenz‘, bzw. allen ,Seins‘. Axiom-4 begründet im GÖDEL-Kalkül das Basis-Theorem-2, <span style="color:#00B000">(wie auch das Korollar-3 von der exklusiven Einzigartigkeit GOTTES)</span>, und ,verankert‘ diese Theoreme damit zugleich in der <span style="color:#4C58FF">,theologisch‘</span>-philosophischen Tradition der ,Rede von GOTT‘ bei ARISTOTELES, — AVICENNA, — ANSELM, — DESCARTES, — LEIBNIZ, — HEGEL, — und bei GÖDEL mit äußerster ,logischer‘ Klarheit.
Anmerkung-5 ''':''' Der ‚Schlüsselbegriff‘ in diesem Kalkül ''':''' <span style="color:#FF6000">„positive Eigenschaft“</span>, bzw. <span style="color:#FF6000">„Vollkommenheit“<span style="color:#00B000"> | </span>„Perfektion“</span>, <span style="color:#4C58FF">— ‚'''P'''‘ —</span>, dominiert alle Axiome des GÖDEL-Kalküls, jedoch ohne inhaltlich genauer ‚bestimmt‘ worden zu sein. Für <span style="color:#4C58FF">— ‚'''P'''‘ — </span> gibt es keine explizite Definition '''!''' <span style="color:#00B000">(Das Theorem-1 ''':''' <span style="color:#4C58FF">— ‚'''PX→◇∃xXx'''‘ —</span>, besagt nur, dass die ,postulierten‘, positiven Eigenschaften, die <span style="color:#FF6000">„Transzendentalia“</span>, formal miteinander verträglich, d.h. ‚widerspruchsfrei‘ sind, wegen Axiom-1 und Axiom-2. Axiom-2 ''':''' <span style="color:#4C58FF">— ‚'''PX ∧ □∀x(Xx→Yx)→PY'''‘ —</span>, besagt, dass positive Eigenschaften ,gleichwertig‘ sind, d.h. gleich ,wahr‘ sind, weil sie ,notwendig‘, <span style="color:#4C58FF">— '''□''' —</span>, aus sich, alle paarweise mit- und voneinander ,impliziert‘ sind, sich gegenseitig ,einschließen‘, und damit eine Einheit bilden, d.h. auch in GOTT ,eins‘ sind. Axiom-2 ist somit zugleich eine ,indirekte‘ Definition für ,positive‘ Eigenschaften ''':''' <span style="color:#4C58FF">— ‚'''P'''‘ —</span>. Definition-2 ''':''' <span style="color:#4C58FF">— ‚'''X'''<sub>ess</sub>'''x ↔ Xx ∧ ∀Y(Yx→ □∀y(Xy→Yy))'''‘ —</span>, besagt ''':''' Weil die ,gleichwertigen‘, positiven Eigenschaften sich gegenseitig implizieren, und damit notwendig von einander abhängen, d.h. koinzident in GOTT ,eins‘ sind, — wenn <span style="color:#4C58FF">‚'''x'''‘</span> für GOTT steht —, dann sind sie somit auch die ,wesentlichen‘ Eigenschaften, die <span style="color:#FF6000">„ultimativen Transzendentalia“</span>, in GOTT, der, wesentlich und exklusiv, notwendig ,Einer‘ ist. Die Definition-2 ist nur dann <span style="color:#FF6000">„modal wahr“</span>, wenn ihre Eigenschafts-Variablen, <span style="color:#4C58FF">— ‚'''X'''‘ —</span> und <span style="color:#4C58FF">— ‚'''Y'''‘ —</span>, mit positive Eigenschaften, d.h. mit <span style="color:#FF6000">„Transzendentalien“</span>, besetzt werden. Diese Definition ist jedoch sofort <span style="color:#FF6000">„modal falsch“</span>, wenn ihre Variablen mit nicht-positive Eigenschaften, wie ,rot‘ oder ,schwer‘ belegt werden, die nicht ,transzendental‘-allgemein gültig sind. Fußnote zu Definition-2 in der GÖDEL-Notiz ''':''' <span style="color:#FF6000">»''any two essences of x are nec. equivalent''«</span>. Diese paarweise, notwendige Äquivalenz von zwei beliebigen Wesenseigenschaften der Individuum-Variable <span style="color:#4C58FF">‚'''x'''‘</span>, wird hier, spezifisch für GOTT, d.h. wenn <span style="color:#4C58FF">‚'''x'''‘</span> für GOTT, dem ,Einen‘, steht, zur <span style="color:#FF6000">„Koinzidenz“</span>, — zum paarweise ,Zusammenfallen aller Gegensätze in Einem‘ —, dem inneren Zusammenhang aller seiner <span style="color:#FF6000">„ultimativen“</span> Vollkommenheiten, d.h. aller <span style="color:#FF6000">„Transzendentalia“</span> und Zuschreibungen, in dem unbegrenzt Unendlichen, GOTT, den schon der Philosoph und Kardinal NIKOLAUS von Kues als <span style="font-family: Times;"><big>,coincidentia oppositorum‘</big></span> verstanden hat.)</span>
In den entscheidenden ‚Schlusssätzen‘ des Kalküls ist der ‚Schlüsselbegriff‘ verschwunden. Hier ist nur mehr von GOTT, <span style="color:#4C58FF">— ‚'''G'''‘ —</span>, die Rede ''':''' Korollar-1, <span style="color:#FF6000">»''Es ist definitiv denkbar, dass es GOTT gibt''«</span>, Theorem-2, <span style="color:#FF6000">»''Dasein, GOTT-Sein, Göttlichkeit ist das Wesen GOTTES''«</span>, Theorem-3, <span style="color:#FF6000">»''Weil GOTT definitiv denkbar, d.h. widerspruchsfrei möglich ist, darum ist auch der Glaube an GOTT widerspruchsfrei, logisch richtig und mathematisch evident, der annimmt, dass es GOTT, mit Notwendigkeit, wirklich gibt''«</span>, <span style="color:#00B000">(nach ANSELM von Canterbury, und was spezifisch das <span style="color:#FF6000">»</span>''Privilegium der Gottheit allein''<span style="color:#FF6000">«</span> ist, nach LEIBNIZ)</span>, und Korollar-3, <span style="color:#FF6000">»''Es gibt notwendig aus sich, d.i. unverursacht, nur einen GOTT''«</span>, <span style="color:#00B000">(nach A. FUHRMANN)</span>. Das GÖDEL-Kalkül ist zu diesen Erkenntnissen gekommen, ohne die Eigenschaften, bzw. die ‚Vollkommenheiten‘ GOTTES, d.h. wer oder was GOTT ‚an sich‘ selbst ist, genauer bestimmen zu müssen, <span style="color:#00B000">(was ,für uns‘ ohnehin ,unmöglich‘ ist)</span>; außer im Theorem-2, in dem das <span style="color:#FF6000">„Dasein“</span> GOTTES als die ‚für uns‘ bestimmende und wichtigste <span style="color:#FF6000">„Wesenseigenschaft“</span> in GOTT erkannt worden ist, — immer vorausgesetzt <span style="color:#00B000">(,angenommen‘)</span>, man ‚glaubt‘ an den zeitlos-ewigen GOTT ''':''' <span style="color:#00B000">(Term :01:)</span>. Der GOTT des GÖDEL-Kalküls ist nicht mehr der an Raum und Zeit gebundene ‚Gott‘ des ARISTOTELES, sondern der von Raum und Zeit <span style="color:#FF6000">»''unabhängige''«</span> GOTT der Bibel bei ANSELM und bei LEIBNIZ. Das GÖDEL-Kalkül, <span style="color:#00B000">(wie ja auch der sog. ‚ontologische Gottesbeweis‘ ANSELMS)</span>, kann jedoch, — bei aller ‚Coolness‘ —, keinen GOTT-Glauben ‚erzeugen‘, sondern setzt vielmehr die Existenz GOTTES notwendig als gegeben schon voraus. Das Kalkül des Logiker GÖDEL beweist aber, dass der traditionelle ‚GOTT-Glaube‘, <span style="color:#FF6000">»''die theologische Weltanschauung''«</span>, mit den Maßstäben der modernen Logik <span style="color:#FF6000">»''durchaus vereinbar''«</span>, d.h. logisch ,richtig‘ und <span style="color:#FF6000">»''mathematisch evident''«</span> ist, weil der ‚Nicht-GOTT-Glaube‘, <span style="color:#FF6000">»''die atheistische Weltanschauung''«</span>, im Möglichkeitsbeweis notwendig zu unlösbaren Widersprüchen führt, und somit logisch ,falsch‘ und unsinnig ist. <span style="color:#00B000">(Die ,Logik‘ hat aber, — bekanntlich —, bei allen wichtigen, persönlichen Entscheidungen immer nur eine untergeordnete Rolle '''!''' )</span>
Anmerkung-6 ''':''' Das erste, ,regulär‘ <span style="color:#00B000">(├ )</span> abgeleitete, Hauptergebnis im 3. Beweisgang, Term :10: <span style="color:#4C58FF">— ‚'''□∃xGx'''‘ —</span> ''':''' der Glaube, dass GOTT ,notwendig‘ existiert, ist die logische Konsequenz aus der Prämisse, Term :1: <span style="color:#4C58FF">— ‚'''Gx'''‘ —</span>. Dieses erste Hauptergebnis hat also den überlieferten, traditionellen GOTT-Glauben zur Voraussetzung, und ist daher davon ,abhängig‘, d.h. Term :10: ist der Ausdruck einer ,Glaubensüberzeugung‘. Das zweite Hauptergebnis im 3. Beweisgang, das Theorem ANSELMS ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx→□∃xGx'''‘ —</span>, dagegen, ist die Darstellung der Abhängigkeit des ersten Hauptergebnisses von dem, vorher schon bewiesenen, ,Axiom‘ von der ,möglichen‘ Existenz GOTTES ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx'''‘ —</span>, dem Möglichkeits-Korollar-1 aus dem 1. Beweisgang. Dem geht folgende Beobachtung voraus ''':''' Der Term ''':''' <span style="color:#4C58FF">— ‚'''□∃xGx'''‘ —</span>, hat in seiner syntaktischen Stellung hier, im Theorem-3, nicht mehr den überlieferten, traditionellen GOTT-Glauben zur Voraussetzung, und ist damit, in seiner Position als Konsequenz-Teil im Theorem ANSELMS, auch nicht mehr davon syntaktisch abhängig, sondern stützt sich formal-syntaktisch nur auf die behauptete Möglichkeit GOTTES; daher muss diese ,angenommene‘ Möglichkeit noch bewiesen <span style="color:#00B000">( ╞ )</span> werden. Dazu die Feststellung LEIBNIZ‘, der diesen Zusammenhang, und seinen Mangel, erkannt hat ''':'''
::Das Theorem ANSELMS ist <span style="color:#FF6000">» ''ein unvollständiger Beweis, der etwas voraussetzt, was man noch hätte beweisen'' <span style="color:#00B000">( ╞ )</span> ''sollen, um ihm mathematische Evidenz zu verleihen — nämlich, dass man dabei stillschweigend voraussetzt, diese Vorstellung des durchaus großen oder durchaus vollkommenen Wesens sei möglich und enthalte keinen Widerspruch'' «</span>.
Diesen <span style="color:#FF6000">»''unvollständigen Beweis''«</span> hat GÖDEL im 1. Beweisgang mit dem ,regulär‘ <span style="color:#00B000">(├ )</span> abgeleiteten, und widerspruchfreien Möglichkeits-Korollar-1, vervollständigt, und damit hat er mit <span style="color:#FF6000">»''mathematischer Evidenz''«</span> bewiesen ''':''' <span style="color:#FF6000">»''Diese Vorstellung des durchaus großen oder durchaus vollkommenen Wesens''«</span> enthält <span style="color:#FF6000">»''keinen Widerspruch''«</span> '''!''' <span style="color:#00B000">(Diese Ergebnis-Zusammenfassung ist eine Vorwegnahme und die Bestätigung für meine Interpretation des Glaubens-Theorem ANSELMS, hier im 3. Beweisgang ''':''' <span style="color:#FF6000">»''Weil es widerspruchsfrei möglich ist, dass es GOTT gibt, ist der Glaube, dass es GOTT wirklich gibt, widerspruchsfrei''«</span>.)</span> Das Möglichkeits-Korollar-1 ist ,regulär‘ nur vom logischen Axiom-1 der Widerspruchsfreiheit, und von der mathematischen Äquivalenz der Perfektionen, <span style="color:#00B000">(der Transzendentalien)</span>, im Axiom-2 ,abhängig‘, und nicht mehr von der ,methodologischen‘ Kalkül-Prämisse, dem traditionellen GOTT-Glauben. Damit hat das Glaubens-Theorem ANSELMS die gesuchte <span style="color:#FF6000">»''mathematische Evidenz''«</span> erreicht, unabhängig von jeder Glaubensüberzeugung. Entscheidend dafür ist der mathematisch-logische Beweis GÖDELS, dass der Nicht-GOTT-Glaube zu einem Widerspruch führt.
Zusammenfassung ''':'''
Theorem-1 ''':''' <span style="color:#4C58FF">— ‚'''PX→◇∃xXx'''‘ —</span> ''':''' <span style="color:#FF6000">»''Positive Eigenschaften sind konsistent''«</span>, ist die logische Konsequenz aus der Prämisse im 1. Beweisgang, Term :01: <span style="color:#4C58FF">— ‚'''PX'''‘ —</span>, den, — <u>modal-frei</u> — gewählten, ,angenommenen‘ positiven Eigenschaften, bzw. ,Transzendentalia‘, <span style="color:#00B000">(die konkret durch Beispiele ,belegbar‘ sind)</span>.
Theorem-2 ''':''' <span style="color:#4C58FF"> — ‚'''Gx↔G'''<sub>ess</sub>'''x'''‘ — </span> ''':''' <span style="color:#FF6000">»'',Da‘-Sein ist das Wesen GOTTES''«</span>, ist die logische Konsequenz aus der Prämisse im 2. Beweisgang, Term :01: <span style="color:#4C58FF">— ‚'''Gx'''‘ —</span>, dem, — <u>modal-frei</u> — gewählten, ,angenommenen‘ GOTT-Glauben der Christen, <span style="color:#00B000">(konkret durch Verweise auf soziologische Gegebenheiten ,belegbar‘, und auch <span style="color:#4C58FF">„theologisch“</span> verifizierbar)</span>.
Im Unterschied dazu ist im 3. Beweisgang, das Theorem-3 ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx→□∃xGx'''‘ —</span> ''':''' der Glaube, dass es einen GOTT notwendig gibt ''':''' <span style="color:#4C58FF">— ‚'''□∃xGx'''‘ —</span>, die logische Konsequenz aus dem, — <u>modal-notwendig</u> — als widerspruchsfrei ,bewiesenen‘, Möglichkeits-Korollar-1 ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx'''‘ —</span>, im 1. Beweisgang, <span style="color:#00B000">(auch im Beweisgang ,Widerlegung‘ im Anhang)</span>, und damit ist das Glaubens-Theorem-3, als ganzes, ,widerspruchsfrei‘. Das Theorem ANSELMS ist, mit Korollar-1, nur vom logischen Axiom-1 der Widerspruchsfreiheit, und der paarweisen, mathematischen Äquivalenz beliebiger positiver Eigenschaften, <span style="color:#00B000">(aller Transzendentalia)</span>, im Axiom-2, abhängig, und nicht mehr von der ,methodologischen‘ Glaubensprämisse ''':''' Term :01:. Damit ist die Bedingung für die geforderte, spezielle <span style="color:#FF6000">»''mathematische Evidenz''«</span>, und auch für die Widerspruchsfreiheit im Glaubens-Theorem ANSELMS erfüllt; unabhängig von jeder Glaubensüberzeugung. Weil es <span style="color:#FF6000">„modal wahr“</span> ist, dass die Transzendentalien, bzw. die Vollkommenheiten ontologisch, — dem Sein nach —‚ alle <span style="color:#00B000">(gegenseitig)</span> widerspruchsfrei, und alle <span style="color:#00B000">(miteinander)</span> gleichwertig sind, ist es auch <span style="color:#FF6000">„modal wahr“</span>, dass es möglich ist, dass GOTT, — dem Sein nach —‚ da ist, <span style="color:#00B000">(d.h. existiert)</span>, der die unbegrenzte Summe aller ultimativen Transzendentalia, bzw. der unendlichen Vollkommenheiten allen Grades ist, wie es der ,christliche Glaube‘ des Erzbischofs ANSELM und seiner Anvertrauten ,annimmt‘.
====<div class="center"><span style="color:#660066">Widerlegung</span></div>====
{|class="wikitable"
|-
! <div class="center"><span style="color:#660066">GÖDEL-Kalkül : der Möglichkeitsbeweis als Widerlegung des Nicht-GOTT-Glaubens</span></div>
|-
! <span style="color:#00B000">''Terme der erweiterten Prädikatenlogik zweiter Stufe_____________ „Benennungen“ und durchgeführte Logik-Operationen''</span>
|-
|
<small>
<math>\begin{align}
\text{(Axiom 1.1)} & \quad P \neg X \;\Longrightarrow\;\ \neg P\ X\ & \ & \text{„Wenn die Negation von X positiv ist, dann ist X nicht positiv“} \\
\text{(Axiom 2)} & \quad \Box \;\forall x (\ X\ x \Longrightarrow \ Y\ x) \Longrightarrow \ P\ Y & \ & \text{„Die Eigenschaft Y in allen x, die aus der Eigenschaft X in allen x} \\
\text{ } & \quad & \ & \; \; \text{mit modaler Notwendigkeit folgt, ist eine positive Eigenschaft“} \\
\text{[RK]} &\quad \Box\; \forall x A \;\Longrightarrow\;\Box\; \forall x B\; \text{ ├ }\;\Box \; \forall x (A \Longrightarrow\; B)\ & \ & \; \; \text{( :: Modales Prinzip KRIPKE-Γ-Ɐx)} \\
\text{(Korollar-1)} &\quad \Diamond \; \exists x \ G \ x \ & \ & \; \; \text{( „das —,x‘— steht für den GOTT, —,G‘—, der Christen“ )} \\
\text{ } & \text{„Es ist möglich, dass es den GOTT der Christen gibt“} & \ & \Longleftarrow\; \text{was zu beweisen ist !} \\
\text{01} & \quad \; \neg \Diamond \; \exists x \ G \ x \ & \ & \text{ AE: „Es ist unmöglich, dass es diesen GOTT gibt“ (dezidierter Atheismus)} \\
\text{02} & \quad (\neg x = .. )\ & \ & \text{ AE: „Es gibt die Eigenschaft, nicht mit x identisch zu sein“ :: (ungleich)} \\
\text{03} & \quad (\ x = .. )\ & \ & \text{ AE: „Es gibt die Eigenschaft, mit x identisch zu sein“ :: (gleich)} \\
\text{04} & \quad \neg\neg \Box \neg \exists x \ G \ x \ & \ & \text{:01:[ ◇A ↔ ¬□¬A ] :: (Modalregel) } \\
\text{05} & \quad \neg\neg \Box \neg\neg \forall x \neg \ G \ x \ & \ & \text{:04:[ ∃xA ↔ ¬Ɐx¬A ] :: (Quantorenregel) } \\
\text{06} & \quad \text{ ├ }\; \Box \; \forall x \neg G \ x \ & \ & \text{:05:NEG :: [ ¬¬A↔A ] :: (Gesetz der Aussagenlogik) } \\
\text{07} & \quad \Box \; \forall x \neg G \ x \Leftrightarrow\ W & \ & \text{:01:06:[ (:01:↔W) → (├:06:↔W) ] :: (Kalkülregel) } \\
\text{08} & \quad \Box \; \forall x \ G \ x \Leftrightarrow\ F & \ & \text{:07:[ (¬A↔W) ↔ (A↔F) ] :: (Regel für Wahrheitswerte)} \\
\text{ } & \text{„Jeder GOTT-Glaube ist notwendig falsch ! “} & \ & \Longleftarrow\; \text{die logische Konsequenz aus der Prämisse :01: !} \\
\text{09} & \quad \Box \; \forall x (\neg x = x ) \Leftrightarrow \ F \; \ & \text{ } & \text{□ⱯxXx:02:Instanz(X:=(¬x=..)) ⇒ Kontradiktion !} \\
\text{10} & \quad \Box \; \forall x (\ G \ x \Rightarrow\; (\neg x = x)) & \text { } & \text{:08:09:[ (:08:↔F) → (:09:↔F) ] :: „ex falso sequitur quotlibet“ :[RK]} \\
\text{11} & \quad \Box \; \forall x (\ G \ x \Rightarrow \; (\neg x = x)) \Rightarrow \; P (\neg x = .. ) & \ & \text{(A2):Instanz(X:=G):Instanz(Y:=(¬x= ..)) } \\
\text{12} & \quad \ P (\neg x = .. ) & \ & \text{:10:11:[ Modus ponens ] :: [ A, A→B ├ B ]} \\
\text{13} & \quad \ P (\neg x = .. )\;\Rightarrow\ \neg P (\ x = .. )\ & \ & \text{(A1.1):Instanz(X:=(x=..))}\\
\text{14} & \quad \neg P (\ x = .. )\ & \ & \text{:12:13:[ Modus ponens ] :: (log. Schlussregel)}\\
\text{15} & \quad \Box \; \forall x \ (x = x ) \Leftrightarrow \ W \; \ & \ & \text{□ⱯxXx:03:Instanz(X:=(x=..)) ⇒ Tautologie !} \\
\text{16} & \quad \Box \; \forall x (\ G \ x \Rightarrow\; (x = x)) & \ & \text{:08:15:[ (:08:↔F) → (:15:↔W) ] :: „ex falso sequitur etiam verum“ :[RK]} \\
\text{17} & \quad \Box \; \forall x (\ G \ x \Rightarrow\; (x = x)) \Rightarrow \ P (x = .. ) & \ & \text{(A2):Instanz(X:=G):Instanz(Y:=( x= ..))} \\
\text{18} & \quad \ P (\ x = .. )\ & \ & \text{:16:17:[ Modus ponens ]}\\
\text{19} & \quad \text{ ├ }\; (\neg P (\ x = .. )\ \wedge \ P (\ x = .. )) \Leftrightarrow\ F & \ & \text{:14:18:[ Konjunktion ] ⇒ Kontradiktion !}\\
\text{20} & \quad \neg \Diamond \; \exists x \ G \ x \Rightarrow (\neg P (\ x = .. )\ \wedge \ P (\ x = .. )) & \ & \text{:01:19:[ ├A├B ╞ A→B ] :: ''KONDITIONALER BEWEIS''}\\
\text{ } & {\color{RedOrange}\text{Der Atheismus führt zu einem logischen Widerspruch ! }} & \ & \Longleftarrow\; \text{was mit Term :20: bewiesen ist !} \\
\text{21} & \quad \neg\neg \Diamond \; \exists x \ G \ x & \ & \text{:20:19:[ Modus tollendo tollens ] :: [ A→B,¬B ├ ¬A ]}\\
\text{22} & \quad \; \Diamond \; \exists x \ G \ x & \ & \text{:21:NEG }\\
\text{(Korollar-1)} & \;\text{„Es ist definitiv möglich, dass es diesen GOTT gibt“} & \ & \Longleftarrow\; \text{was zu beweisen war !} \\
\end{align}</math>
</small>
|}
Anmerkung-7 ''':''' <span style="color:#00B000">( Dieser Beweisgang ist praktisch identisch mit dem 1. Beweisgang, jedoch gestrafft und präzisiert nach G.J.WIRSCHING. Diese Widerlegung des Nicht-GOTT-Glaubens beginnt sofort mit dem Korollar-1 <span style="color:#4C58FF">— ‚'''◇∃xGx'''‘ —</span> aus dem 1. Beweisgang, als in Frage gestelltes ,Theorem‘, und beweist, ohne das Axiom-3 <span style="color:#4C58FF">— ,'''PG'''‘ —</span>, und ohne die GÖDEL-Prämisse <span style="color:#4C58FF">— ,'''PX'''‘ —</span>, in einem Zwischenergebnis, die innere Widersprüchlichkeit und damit auch die Unhaltbarkeit der atheistischen Glaubens-Annahme, aus rein logischen ,Gründen‘, bzw. Axiomen. )</span>
Dieser Beweisgang geht von der Existenz GOTTES, <span style="color:#4C58FF">— ,'''G'''‘ —</span>, als Glaubens-Annahme, aus, wobei aber die Möglichkeit seiner Existenz, und damit die Sinnhaftigkeit des Glaubens an GOTT, durch die Prämisse :01: in Frage gestellt wird, und daher im Kalkül überprüft werden muss. Denn mit der Behauptung der Existenz GOTTES allein ist es nicht getan. Es muss auch seine ,Möglichkeit‘, d.h. die Sinnhaftigkeit des Glaubens an GOTT bewiesen werden. LEIBNIZ hat als erster, <span style="color:#00B000">(nach ANSELM)</span>, dieses Problem gesehen, und GÖDEL hat dafür die Lösung gefunden. Dieser Beweisgang, <span style="color:#00B000">(analog zum Möglichkeitsbeweis von Günther J. WIRSCHING konzipiert)</span>, setzt in den Axiomen, genau wie im 1. Beweisgang, die Existenz von etwas <span style="color:#FF6000">„Positiven“, „Perfekten“, „Vollkommenen“</span>, <span style="color:#4C58FF">— ,'''P'''‘ —</span>, <span style="color:#00B000">(d.h. die Transzendentalien)</span>, allgemein für die Welt voraus, <span style="color:#00B000">(was im Axiom-3 ''':''' <span style="color:#4C58FF">— ,'''PG'''‘ —</span>, auch GOTT ultimativ zugeordnet wird ''':''' <span style="color:#FF6000">»''GOTT ist absolut positiv, perfekt und vollkommen''«</span>)</span>; das dann im 2. Beweisgang mit Term :13: als widerspruchsfrei, <span style="color:#00B000">(als ,wahr‘ und ,annehmbar‘ im Kontext des christlichen Glaubens)</span>, schon ,bewiesen‘ <span style="color:#00B000">( ╞ )</span> worden ist. Die Existenz von ,Transzendentalien‘ in der Welt, ist ein, mit Beispielen belegbares, allgemeines Faktum; ihre Existenz auch in GOTT ist mit dem Term :13: des 2. Beweisganges bewiesen, die jedoch im Unendlichen, GOTT, als Transzendentalia, auch in ,ultimativer‘ Form vorliegen. Axiom-1 ,besagt‘, dass Eigenschaften nicht zugleich, vollkommen und nicht vollkommen, sein können. Axiom-2 ,besagt‘, dass, allgemein, alle Vollkommenheiten, <span style="color:#00B000">(alle Transzendentalien)</span>, gleichwertig, <span style="color:#00B000">(mathematisch äquivalent)</span>, sind. <span style="color:#00B000">(Axiom-2 wird hier um das GÖDEL-Konjunkt <span style="color:#4C58FF">— ,'''PX'''‘ —</span> verkürzt dargestellt. Damit ist auch Axiom-3 für diesen Beweisgang unnötig geworden, ohne dass sich wegen dieser Kürzung am Ergebnis etwas ändert. Das Axiom-2 wird jedoch sofort <span style="color:#FF6000">„modal falsch“</span>, wenn seine Variablen mit nicht-positive Eigenschaften, wie ,rot‘ oder ,schwer‘ belegt werden, die nicht ,transzendental‘-allgemein gültig sind, die keine ,Transzendentalia‘ sind.)</span> Die Eigenschaft <span style="color:#4C58FF">— (¬x=..) —</span> ,besagt‘, dass GOTT <span style="color:#FF6000">„unvergleichlich“</span> ist, wenn <span style="color:#4C58FF">,'''x'''‘</span> für GOTT steht. <span style="color:#00B000">(Der informelle Term, <span style="color:#4C58FF">— (¬x=..) —</span>, ersetzt hier, wie bei A. FUHRMANN, den formal korrekten Abstraktionsausdruck ''':''' <span style="color:#4C58FF">— λy.(¬x=y) —</span>, aus dem Lambda-Kalkül.)</span> Der Term :16: <span style="color:#4C58FF">— (x=x) ↔ W —</span> steht auch für die Selbstbezeichnung des GOTTES-JHWH in Exodus 3,14 ''':''' <span style="color:#CC66FF">»''Ich bin derjenige, der ‚IST‘'' <span style="color:#00B000">[ aus mir selbst ]</span>«</span>, der Grund für die <span style="color:#FF6000">„Unvergleichbarkeit“</span> GOTTES. Der GOTT der abendländischen, christlichen Tradition wird mit <span style="color:#4C58FF">— ,'''G'''‘ —</span> bezeichnet ''':''' d.i. der <span style="color:#FF6000">„GOTT der Christen“</span>, entsprechend der ,methodologischen‘ Prämisse und der ,Genese‘ des Kalküls, syntaktisch formalisiert in der Definition-1 ''':''' <span style="color:#4C58FF">— ‚'''Gx↔∀X(PX→Xx)'''‘ —</span> ''':''' <span style="color:#FF6000">»''Das Individuum'' <span style="color:#4C58FF">,'''x'''‘</span> ''ist genau dann GOTT'', <span style="color:#4C58FF">,'''G'''‘</span>, ''wenn es alle Vollkommenheiten'', <span style="color:#4C58FF">,'''P'''‘</span>, ''in sich schließt''«</span>, nach der Vorgabe von LEIBNIZ ''':''' <span style="color:#FF6000">»''GOTT ist ein Wesen von äußerster Größe und Vollkommenheit, das alle Grade derselben in sich schließt''«</span>. Mit Korollar-1 hat dieser Beweisgang dasselbe Endergebnis, wie der 1. Beweisgang. Der Beweis, dass der dezidierte Atheismus zu einem logischen Widerspruch führt, und damit falsch und unsinnig ist, ist ein Zwischenergebnis in diesem Beweisgang, und begründet mit <span style="color:#FF6000">»''mathematischer Evidenz''«</span>, und unabhängig von jeder Glaubensüberzeugung, den, von LEIBNIZ gesuchten, Möglichkeitsbeweis für die Existenz GOTTES im Argument des Erzbischofs, und bestätigt damit die Sinnhaftigkeit des GOTT-Glaubens. Einmal Axiom-1 und zweimal Axiom-2 sichern hier das Ergebnis des Kalküls ''':''' das Möglichkeits-Korollar-1 ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx'''‘ —</span> ''':''' <span style="color:#FF6000">»''Es ist definitiv möglich, dass es den GOTT der Christen gibt''«</span>. Axiom-1 beruht auf der gegenseitigen Widerspruchsfreiheit aller Transzendentalia. Axiom-2 beruht auf der wechselseitigen Äquivalenz, bzw. der Gleichwertigkeit aller Transzendentalia. Diese zwei Axiome sind die einzigen, und modal-notwendigen, d.h. die transzendentalen Voraussetzungen und Bedingungen für das Endergebnis ''':''' der Widerspruchsfreiheit und Sinnhaftigkeit des Glaubens der Christen an GOTT; <span style="color:#00B000">(dasselbe gilt natürlich auch für die <span style="color:#4C58FF">„theologische“</span> Weltanschauung jeder monotheïstischen Religion '''!''' Dem Erzbischof ANSELM ging es damals nur um seinen Glauben an GOTT.)</span>. Die Existenz der Transzendentalien ''':''' <span style="color:#4C58FF">— ‚'''□∀xXx'''‘ —</span>, ist ein Faktum unserer Welt, das mit Beispielen belegbar ist ''':''' Transzendentalien gibt es, als ,empirisch‘-sprachliche Grundlage des ontologischen ,Beweises‘, genau so, wie es auch Sterne gibt. Darauf beruht die Stringenz der <span style="color:#4C58FF">»''axiomatischen Theologie''«</span> GÖDELS. Damit ist es ihm endgültig gelungen, eine eminent geisteswissenschaftliche Frage, die Jahrhunderte hindurch, lang kontrovers war, einer Lösung zuzuführen, die nun unwiderruflich gültig ist, und es auch bleiben wird.
Die Logik-Regel <span style="color:#FF6000"><span style="font-family: Times;"><big>„ex falso sequitur quotlibet, etiam verum“</big></span></span>, <span style="color:#00B000">(‚Aus Falschem folgt irgendetwas, auch Wahres‘)</span>, ist der scholastische Ausdruck für die ‚Implikation‘ <span style="color:#00B000">(Folgerung)</span> von Aussagen, die nur dann falsch, <span style="color:#4C58FF">— F —</span> ist, wenn das Antezedens wahr, <span style="color:#4C58FF">— W —</span>, und die Konsequenz falsch, <span style="color:#4C58FF">— F —</span> ist. Andernfalls ist sie immer wahr, <span style="color:#4C58FF">— W —</span>, auch wenn die Voraussetzung falsch, <span style="color:#4C58FF">— F —</span> ist ''':''' ‚Modern‘ darstellbar durch die ‚Wahrheitswertetafel‘ für die ‚materiale Implikation‘, <span style="color:#4C58FF">— ,(A → B)‘ —</span> <span style="color:#FF6000">„wenn A, dann B“</span>. Damit ist auch der <span style="color:#4C58FF">[ Modus tollendo tollens ] :: [ A → B, ¬B ├ ¬A ] </span> verstehbar; <span style="color:#00B000">(vgl. die vierte Zeile der ‚materialen Implikation‘)</span>. Der positive hypothetische Syllogismus ''':''' <span style="color:#4C58FF">[ Modus ponendo ponens ] :: [ A → B, A ├ B ] </span> ist aus der ersten Zeile ablesbar.
Die folgende Tabelle gibt für jeden ,Wahrheitswert‘ der Aussagen <math>A</math> und <math>B</math> das Resultat einiger zweiwertiger Verknüpfungen an ''':'''
{|class="wikitable hintergrundfarbe2" style="text-align:center;"
|-
!colspan="2"|''Belegung''!!Konjunktion!!Disjunktion!!materiale<br /> Implikation!!Äquivalenz<br /> Bikonditional!!kopulative<br /> Konjunktion
|-
!<math>A</math>
!<math>B</math>
!<math>A</math> und <math>B</math>
!<math>A</math> oder <math>B</math>
!wenn <math>A</math> dann <math>B</math>
!sowohl <math>A</math> als auch <math>B</math>
!entweder <math>A</math> oder <math>B</math>
|-
!W!!W
|W||W||W||W||F
|-
!W!!F
|F||W||F||F||W
|-
!F!!W
|F||W||W||F||W
|-
!F!!F
|F||F||W||W||F
|}
<span style="color:#00B000">(Eine ‚Konjunktion‘ ist nur dann ,wahr‘, wenn beide Aussagen einer ‚Konjunktion‘ wahr sind. Eine ‚kopulative Konjunktion‘ ist nur dann ,wahr‘, wenn entweder die eine, oder die andere Aussage der ‚kopulativen Konjunktion‘ wahr ist. Es besteht also eine Wenn-Dann-Verbindung zwischen beiden Aussagen — eine ,Kopplung‘. Das ist die logische Grundlage von Axiom-1 im GÖDEL-Formalismus)</span>
Um das Widersprüchliche der ,Annahme‘ nachzuweisen, dass positive Eigenschaften ,nicht konsistent‘ seien, <span style="color:#00B000">(im 1. Beweisgang)</span>, bzw. um das Unlogische und Sinnwidrige der ,Annahme‘ klarzustellen, es sei ,unmöglich‘, dass es einen GOTT gibt, <span style="color:#00B000">(hier, in der Widerlegung)</span>, verwendet das GÖDEL-Kalkül den Gegensatz ''':''' wahr, <span style="color:#4C58FF">— W —</span>, falsch, <span style="color:#4C58FF">— F —</span>, zwischen der dritten und vierten Zeile der Wahrheitswertetafel, für zwei ,wahre‘ <span style="color:#4C58FF">— W —</span> ,materiale Implikationen‘, in denen die Voraussetzung jeweils falsch, <span style="color:#4C58FF">— F —</span>, ist, entsprechend der Regel <span style="color:#FF6000"><span style="font-family: Times;"><big>„ex falso, <span style="color:#4C58FF">— F —</span>, sequitur quotlibet, <span style="color:#4C58FF">— F —</span>, etiam verum, <span style="color:#4C58FF">— W —</span>“</big></span></span>, jeweils mit der Hilfe von Axiom-1 und Axiom-2, in einer <span style="font-family: Times;"><big>,Reductio ad absurdum‘</big></span>; hier unabhängig von jeder Glaubensüberzeugung. Im Gegensatz dazu, wird, <span style="color:#00B000">(im 2. Beweisgang)</span>, aus dem Glauben an GOTT, auch mit einer <span style="font-family: Times;"><big>,Reductio ad absurdum‘</big></span>, speziell mit Axiom-1, das Widersprüchliche in der ,Annahme‘ nachgewiesen, es gäbe in GOTT <span style="color:#FF6000">»''keine Vollkommenheit, nichts Positives, nichts Perfektes''«</span>, d.h. keine ,ultimativen Transzendentalia‘. In dieser <span style="font-family: Times;"><big>,Reductio ad absurdum‘</big></span>, im 2. Beweisgang, wird vorausgesetzt <span style="color:#00B000">(,angenommen‘)</span> ''':''' es gibt den GOTT der Christen, <span style="color:#00B000">(als Prämisse :01:)</span>, der ,unvergleichlich‘ und ,einzigartig‘ ist, und in dem auch alle ,Transzendentalia‘ <span style="color:#FF6000">„koinzident“</span> ,eins‘ sind, entsprechend Axiom-2.
Für KANT entsteht ein Widerspruch in den Prädikaten eines Satzes.
::<span style="color:#FF6000">»</span> ''Wenn ich das Prädicat in einem identischen Urtheile aufhebe'', <span style="color:#00B000">[ durch eine Negation ]</span>, ''und behalte das Subject, so entspringt ein Widerspruch''. <span style="color:#00B000">[ Wenn ich sage ''':''' ,''GOTT ist nicht allmächtig''‘, entsteht ein Widerspruch zur richtigen Aussage ''':''' ,''GOTT ist allmächtig''‘. ]</span> … ''Wenn ihr aber sagt ''':''' ,GOTT ist nicht‘, so ist weder die Allmacht, noch irgendein anderes seiner Prädicate gegeben; denn sie sind alle zusammt dem Subjecte aufgehoben'', <span style="color:#00B000">[ negiert ]</span>, ''und es zeigt sich in diesem Gedanken nicht der mindeste Widerspruch.'' <span style="color:#FF6000">«</span><ref>vgl. ‚''<span style="font-family: Times;"><big>Kritik der reinen Vernunft</big></span>''‘, Seite 398f; https://www.korpora.org/kant/aa03/398.html</ref>
Es ist richtig, wie KANT sagt, der Widerspruch entsteht nicht in dem Gedanken ''':''' <span style="color:#FF6000">»</span>''GOTT ist nicht''<span style="color:#FF6000">«</span>. GÖDEL zeigt daher, dass der Widerspruch erst dann entsteht, wenn von der Annahme ausgegangen wird ''':''' ''<span style="color:#FF6000">»</span>Es ist unmöglich, dass GOTT ist''<span style="color:#FF6000">«</span>. Daraus folgt dann ,regulär‘, mit Hilfe von Axiom-1 und Axiom-2, <span style="color:#00B000">(d.h. mit den Theoremen von den Transzendentalien)</span>, die logische ,Möglichkeit‘ GOTTES, unabhängig von jeder Glaubensüberzeugung. Wie LEIBNIZ klar erkannt hat, muss zuerst, <span style="color:#00B000">(mit GÖDEL ''':''' aus dem Widerspruch des Gegenteils)</span>, die logische ,Möglichkeit‘, <span style="color:#00B000">(die Konsistenz)</span>, der Existenz GOTTES bewiesen werden ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx'''‘ —</span>, bevor daraus die reale ,Notwendigkeit‘ GOTTES abgeleitet werden kann ''':''' <span style="color:#4C58FF">— ‚'''□∃xGx'''‘ —</span>, dessen ,Existenz‘ jedoch, aus dem ,Glauben‘, im Voraus, schon angenommen worden ist. Dieser Sachverhalt ist das ausschließliche Spezifikum GOTTES, <span style="color:#00B000">(das <span style="color:#FF6000">»''Privilegium der Gottheit allein''«</span>)</span>, und gilt nur bei GOTT, als dem Unvergleichlichen und Einzigartigen. Dieses ,Spezifikum‘ wird im Theorem ANSELMS abgebildet ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx→□∃xGx'''‘ —</span>, auf Grund von Axiom-2, das den inneren Zusammenhang, die Koinzidenz auch von ,Möglichkeit‘ und ,Notwendigkeit‘ im Unendlichen, GOTT, erkennen lässt. Bis Zeile 10, im 3. Beweisgang, reicht der Geltungsbereich der ,modal‘-frei gewählten Kalkül-Prämisse :01:, der ,methodologische‘ GOTT-Glaube. In Zeile 11 liegt der ,Schwerpunkt‘ des ontologischen Beweises dann aber am, — modal als notwendig — ,bewiesenen‘ Widerspruch des Gegenteils zum GOTT-Glauben, <span style="color:#00B000"> (formal-syntaktisch dargestellt als widerspruchfreies Antezedens, <span style="color:#4C58FF">— ‚'''◇∃xGx'''‘ —</span>, dem Möglichkeits-Korollar-1 aus dem 1. Beweisgang)</span>, und nicht mehr am ,angenommenen‘ GOTT-Glauben der Kalkül-Voraussetzung, <span style="color:#00B000">(nun dargestellt als Konsequenz <span style="color:#4C58FF">— ‚'''□∃xGx'''‘ —</span> im Theorem ANSELMS)</span>. Das Möglichkeits-Korollar-1 muss jetzt immer, wie folgt, gelesen werden ''':''' <span style="color:#FF6000">»''Es ist <u>widerspruchsfrei</u> möglich, dass GOTT ist''«</span>. Damit hat GÖDEL, — angeregt durch LEIBNIZ, und mit ihm —, die fast einhellig akzeptierte Fehldeutung des ontologisch-<span style="color:#4C58FF">„theologischen“</span> Arguments ANSELMS für GOTT durch gewichtige philosophische, <span style="color:#00B000">(KANT<ref>GOTT ist absolut einzigartig und unvergleichlich. KANT macht GOTT jedoch zu einem ,Ding‘ unter den vielen ,Dingen‘ dieser Welt, indem er die Existenz, bzw. das ,Sein‘ GOTTES mit dem ,Sein der Dinge‘ gleich setzt. Eine solche Gleichsetzung ist bei GOTT unangebracht und daher unzulässig! KANT verkennt damit die Einzigartigkeit und Besonderheit GOTTES. Das ,Sein‘ der Dinge ist, — wie KANT sagt —, kein ,reales Prädikat’, d.h. Existenz ist keine Eigenschaft. Jedoch in GOTT ist ,Sein‘ ein ,reales Prädikat‘, d.h. Existieren ist ,real‘ die Wesenseigenschaft GOTTES, denn GOTT ist der, der für uns, — aus Liebe —, immer schon ,da‘ ist ''':''' in der ,Hypostatischen Union‘, sowohl als GOTT von Ewigkeit zu Ewigkeit, als auch als Mensch in unserer Zeitlichkeit. Das ist das, was GOTT für uns ausmacht —, sein Wesen. Eine ,Theologie‘, die solches sagt, und die, auch als vertrauenswürdige ,Wissenschaft von GOTT‘, akzeptiert wird, hat Interpretations-Vorrang, jedesmal, wenn es um die ,Rede von GOTT‘ geht. Das war für KANT jedoch ,methodologisch‘ weder denkbar, noch nachvollziehbar.</ref>)</span>, und <span style="color:#4C58FF">„theologische“</span>, <span style="color:#00B000">(THOMAS<ref>GOTT ist absolut einzigartig und unvergleichlich. THOMAS unterscheidet die ,Natur GOTTES‘ nicht von der ,Natur der Dinge‘, indem er die ,Natur‘ des GOTTES ANSELMS irrtümlich mit der ,Natur‘ der Dinge gleich setzt. Damit reiht er GOTT unter die vielen Dinge unserer Welt ein, indem er für den GOTT ANSELMS annimmt ''':''' GOTT ,esse in rerum natura‘, d.h. wörtlich, dass der GOTT ANSELMS in der ,Natur‘ der Dinge existiert, <span style="color:#00B000">(was ANSELM nie gesagt hat)</span>. Eine solche Gleichsetzung ist bei GOTT unangebracht und daher unzulässig! Die ,zeitlose-überzeitliche‘ Wirklichkeit, (Natur), GOTTES ist völlig verschieden und unabhängig von der zufälligen Wirklichkeit, (die ,Natur‘), unserer ,raum-zeitlichen‘ Welt. Daher ist sie mit dieser auch nicht vergleichbar. Thomas verkennt damit die Einzigartigkeit und Besonderheit GOTTES.</ref>)</span>, Autoritäten zurechtgerückt, welche die Einzigartigkeit und Unvergleichlichkeit des Unendlichen, GOTT, bei ihrer Beurteilung des Theorem ANSELMS nicht berücksichtigt, sondern den Unendlichen, <span style="color:#00B000">(irrtümlich)</span>, unter die endlichen Dinge unserer Welt eingereiht haben. Weder THOMAS, noch KANT, haben verstanden, worum es ANSELM, mit der Aufstellung seines Theorems, eigentlich ging. Beide meinten <span style="color:#00B000">(irrtümlich)</span>, ANSELM wolle mit seinem Theorem die ,Existenz‘ GOTTES ,beweisen‘. Die Existenz GOTTES wurde von ANSELM nie in Frage gestellt. Für ANSELM wäre es rationaler ,Unsinn‘, einen Beweis für die Existenz GOTTES verlangen zu wollen. Mit ,Beweisen‘ wird es niemals möglich sein, einen GOTT-Glauben ,erzeugen‘, bzw. ,lukrieren‘ zu können, <span style="color:#00B000">(was man ohnehin weiss, und immer schon gewusst hat; Glaube ist ein Geschenk, das man erhält, wenn man darum bittet)</span>. Für ANSELM ging es vielmehr um die Frage, ob es logisch ,sinnvoll‘ und ,vernünftig‘ ist, an GOTT zu glauben; und kein ,Sprung‘ ins Ungewisse, d.h. ob <u>der Glaube</u> rational und ,zweifellos‘ vertrauenswürdig ist, und das heißt auch, ob <span style="color:#4C58FF">„Theologie“</span> ,rational‘ und vertrauenswürdig als Wissenschaft betrieben werden kann. GÖDEL hat mit dem bewiesenen Widerspruch des Gegenteils zum GOTT-Glauben, den Beweis für die <span style="color:#FF6000">»''mathematisch-logische Evidenz''«</span>, und damit auch für die ,Vernünftigkeit‘, bzw. ,Rationalität‘ des Theorem ANSELMS geliefert, was, nach LEIBNIZ, für die Akzeptanz dieses Theorems noch gefehlt hat. Das Theorem ANSELMS besagt universell ''':''' Die <span style="color:#FF6000">»''theologische Weltanschauung'', <span style="color:#00B000">[ d.h. auch ''':''' der ,GOTT-Glaube‘ ]</span>«</span> der Juden, Christen und Muslime, <span style="color:#00B000">(und auch des ägyptischen Pharaos ECHNATON und seiner Frau NOFRETETE)</span>, die ,annehmen‘, dass es mit ,Notwendigkeit‘ <span style="color:#4C58FF">— ‚'''□'''‘ —</span> <span style="color:#00B000">(nur)</span> einen GOTT gibt, ist logisch richtig, ,vernünftig‘, und <span style="color:#FF6000">»''mathematisch evident''«</span>, weil es <u>ohne Widerspruch</u> ,denkbar‘ <span style="color:#4C58FF">— ‚'''◇'''‘ —</span> ist, dass es GOTT gibt ''':''' Nicht mehr und nicht weniger, <span style="color:#FF6000">»''rein verstandesmäßig, (ohne sich auf den Glauben an irgendeine Religion zu stützen)''«</span>. Das ist auch eine Rechtfertigung der <span style="color:#4C58FF">„Theologie“</span> als seriöse, <span style="color:#FF6000">»''rein verstandesmäßige''«</span> Wissenschaft.
Es geht hier bei GÖDEL nicht um Theoriefindung oder ähnliches. GÖDEL ist kein ,spekulierender‘ Theoretiker. GÖDEL ist Logiker und Mathematiker. Was er sagt, ist mathematisch wahr und logisch richtig. Wenn er sagt, dass die Aussage ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx'''‘ — »''wahr''«</span> ist ''':''' <span style="color:#FF6000">»''es ist möglich, dass es Gott gibt'' ''':''' ''das ist ,wahr‘ wegen Axiom-2 (und Axiom-1)''«</span>, dann spricht er hier von der mathematischen Wahrheit. Logischerweise ist dann die konträre Aussage ''':''' <span style="color:#4C58FF">— ‚'''¬◇∃xGx'''‘ — »''falsch''«</span> ''':''' <span style="color:#FF6000">»''es ist nicht möglich, dass es einen Gott gibt''«</span>, und zwar mathematisch falsch, weil sich aus dieser Aussage ein Widerspruch ergibt. Jeder, der die mathematische Logik GÖDELS lesen kann, kann das sehen und verstehen ''':''' Das Zwischenergebnis, <span style="color:#00B000">(Term :20:)</span>, in dieser Kalkül-Ableitung, die logische Konsequenz aus der Annahme des dezidierten Atheismus, es sei unmöglich, dass es GOTT gibt, ist der faktische, nachprüfbare, und für jeden Menschen sichtbare Beweis dafür, dass diese Annahme in einen Widerspruch mündet, und damit falsch und unlogisch ist. Das bedeutet, es ist eine Tatsache, bzw. es ist ein allgemein gültiges Faktum unserer Welt, dass der Atheismus, — mit <span style="color:#FF6000">»''mathematisch-logischer Evidenz''«</span> —, wirklich falsch und rational unsinnig ist, und daher, mit Recht, als ,Unsinn‘ bezeichnet werden darf '''!''' Das ist nicht bloß als eine Theoriefindung, oder als eine Interpretation eines Autors zu verstehen. Das ist vielmehr genau so wahr und <span style="color:#FF6000">»''mathematisch evident''«</span>, wie, dass zwei mal zwei vier ergibt, und wirklich genau so logisch richtig, wie, dass die Erde sich um die Sonne dreht. Das ist gerade das Überraschende und Unerwartete am Gödel-Kalkül. Es geht hier nicht mehr um Theoriefindung oder Interpretationen, denen man zustimmen kann oder nicht. Es geht hier <span style="color:#FF6000">»''rein verstandesmäßig''«</span> um mathematisch-logische Fakten. Damit steht GÖDEL, in seiner Bedeutung, in gewisser Weise, neben KOPERNIKUS, weil seine Kalkül-Ergebnisse, wie die Berechnungs-Ergebnisse dieses Pioniers der damaligen Zeitenwende, heute, in unserem Äon, ebenfalls mit eingefleischte Vorurteile aufräumen, ohne aber, dass GÖDEL für sich selbst, im Glauben, logisch konsequent gewesen wäre. <span style="color:#00B000">(Er ist sich der Tragweite und der Konsequenzen seiner <span style="color:#4C58FF">„axiomatischen Theologie“</span> anscheinend nicht bewusst gewesen, wenn er sagt ''':''' Man sei <span style="color:#FF6000">»''heute noch weit davon entfernt, das theologische Weltbild wissenschaftlich begründen zu können''«</span>, nachdem sein Kalkül das <span style="color:#FF6000">„atheistische Weltbild“</span> fundamental ins Wanken gebracht und als irrationalen ,Unsinn‘ entlarvt hat. Sein Kalkül ,beweist‘ ( ╞ ) indirekt, dass das <span style="color:#FF6000">»''theologische Weltbild''«</span>, im Beweisgang ,Widerlegung‘, aus dem Widerspruch des Gegenteils, mit <span style="color:#FF6000">»''mathematisch-logischer Evidenz''«</span>, offensichtlich doch, wissenschaftlich begründet werden kann)</span>.
----
Kurt GÖDEL ist auch deswegen ein Ausnahmelogiker.
----
===<div class="center"><span style="color:#660066">Epilog für Skeptiker</span></div>===
Wenn man das GÖDEL-Argument genau liest, dann ist nur die Annahme ''':''' <span style="color:#FF6000"> „es ist möglich, dass es GOTT gibt“</span> bewiesen, weil aus der Annahme ''':''' <span style="color:#FF6000">„es ist unmöglich, dass es GOTT gibt“</span> ein logischer Widerspruch ableitbar ist. Die Aussage ''':''' <span style="color:#FF6000">„es gibt GOTT“</span> ist dagegen schon eine Glaubensaussage, und damit ist das auch die ,Grundannahme‘ eines gläubigen Menschen, der dann aus der ,bewiesenen Möglichkeit‘, dass es Gott gibt, ableiten kann ''':''' <span style="color:#FF6000">»''es gibt GOTT wirklich''«</span>, wenn er will ''':''' <span style="color:#FF6000">»''Es stimmt also, was ich glaube '''!''''' «</span> Das ist das Argument ANSELMS, der ein christlicher Amtsträger war, und der daher von dieser Grundannahme auch ausgeht. Solange in den Voraussetzungen des Möglichkeitsbeweises im GÖDEL-Kalkül kein Widerspruch nachweisbar ist, und solange in der logischen Durchführung dieses Beweises keine schweren Mängel festgestellt werden können, ist das Ergebnis des Möglichkeitsbeweises, wie GÖDEL ihn durchgeführt hat, korrekt, und die Folgerungen daraus, logisch richtig, dass es sich hier um <span style="color:#FF6000">»''mathematische Evidenz''«</span> handelt, <span style="color:#00B000">(z. B. wie 2 x 2 = 4)</span>. Aber niemand ist gezwungen, aus der Möglichkeit, dass es GOTT gibt, daraus zu schließen, dass es GOTT auch mit Notwendigkeit gibt, wie das im Argument ANSELMS geschieht, außer, er akzeptiert auch die Grundannahme, dass es den Unendlichen und Unvergleichlichen tatsächlich gibt. Dann kann er mit LEIBNIZ, der selbst an GOTT geglaubt hat, mit Bestimmtheit sagen ''':''' <span style="color:#FF6000">»''gesetzt, dass GOTT möglich ist, so ist er, was das Privilegium der Gottheit allein ist''«</span>, weil GÖDEL mit seinem Kalkül den noch ausstehenden Beweis der Widerspruchsfreiheit dafür geliefert hat.
Wenn Du den 3. Beweisgang des GÖDEL-Kalküls genauer anschaust, dann siehst Du, dass der Konsequenz-Teil im Argument ANSELMS ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx→□∃xGx'''‘ —</span>, der identisch ist mit dem Term in der Zeile 10, <span style="color:#4C58FF">— ‚'''□∃xGx'''‘ —</span>, immer noch logisch, <span style="color:#00B000">(jedoch nicht syntaktisch)</span>, abhängig ist von der methodologischen Glaubens-Prämisse, Term :01:, <span style="color:#4C58FF">— ‚'''Gx'''‘ —</span> ''':''' <span style="color:#FF6000">»''das'' <span style="color:#4C58FF">‚'''x'''‘</span> ''steht für den Gott der Christen''«</span>. Diese Abhängigkeit ist bis zur Zeile 10 offensichtlich und logisch korrekt. <span style="color:#00B000">(Man könnte nach dieser Zeile, ohne Weiteres, ,regulär‘ die <span style="color:#FF6000">„logische Implikation”</span> :: <span style="color:#4C58FF">[├ A ├ B ╞ A→B ]</span> mit Term :01: und Term :10: als ein mögliches Korollar bilden ''':''' <span style="color:#4C58FF">— ‚'''Gx→□∃xGx'''‘ — </span>)</span>. Das bedeutet, der Konsequenz-Teil im Theorem ANSELMS, der in diesem ,regulär‘-möglichen Korollar an zweiter Stelle steht, ist damit in seiner Formal-Struktur offensichtlich ,regulär‘ von der Glaubens-Prämisse, Term :01:, abhängig, d.h. er ist der Ausdruck einer Glaubensüberzeugung, als 1. Hauptergebnis. Damit ist syntaktisch bewiesen, die Existenz GOTTES ist die ,notwendige‘ <span style="color:#4C58FF">— ‚□‘ —</span> Konsequenz aus dem ,methodologischen‘ Glauben im Term :01:. Im Theorem ANSELMS steht dieses 1. Hauptergebnis nun, in der Zeile 11, als Konsequenz-Teil, auch an zweiter Stelle, hat aber nicht mehr seine Glaubens-Prämisse, als seine ,notwendige‘ Bedingung, an erster Stelle vor sich, die seinen Inhalt bestätigt, wie vorhin, im ,regulär‘-möglichen Korollar. Jetzt steht eine neue und andere Voraussetzung als syntaktische Begründung vor ihm. Der Schwerpunkt des Argument ANSELMS liegt damit am Begründungs-Teil des ANSELM-Theorems, der jetzt die erste Stelle im Theorem einnimmt ''':''' <span style="color:#4C58FF">— ‚'''◇∃xGx'''‘ —</span> ''':''' <span style="color:#FF6000">»''es ist möglich, dass es GOTT gibt''«</span>, der erst dadurch entstanden ist, und davon abhängig ist, weil sein Gegenteil ''':''' <span style="color:#4C58FF">— ‚'''¬◇∃xGx'''‘ —</span> ''':''' <span style="color:#FF6000">»''es ist unmöglich, dass es GOTT gibt''«</span>, zu einem Widerspruch geführt hat. Dieser Begründungs-Teil, das Antezedens, im Argument ANSELMS, ist daher nicht mehr von der ,methodologischen‘ Glaubens-Prämisse, Term :01:, abhängig, sondern nur vom Axiom-1, der Widerspruchsfreiheit, und von der paarweisen, mathematischen Äquivalenz beliebiger positiver Eigenschaften im Axiom-2, die im 1. Beweisgang, bzw. im Beweisgang ,Widerlegung‘, mit dem <span style="color:#4C58FF">[ Modus tollendo tollens ] :: [ A → B, ¬B ├ ¬A ]</span>, die Widerspruchsfreiheit der Möglichkeit des GOTT-Glaubens mit Notwendigkeit herbeigeführt haben. Daraus ergibt sich eine logische Verschiebung in der Argumentationskette, denn dieser Begründungs-Teil, der jetzt die Widerspruchsfreiheit für den Konsequenz-Teil liefert, ist selbst unabhängig und frei von jeder Glaubensüberzeugung. Weil widerspruchsfrei und <span style="color:#FF6000">»''mathematisch evident''«</span> und frei von jeder Glaubensüberzeugung, muss er als rein logische Begründung für die Widerspruchsfreiheit, und als Beweis für die <span style="color:#FF6000">»''mathematische Evidenz''«</span> des Konsequenz-Teils gelesen werden, und damit bestätigt er die Widerspruchsfreiheit und Sinnhaftigkeit der Glaubensüberzeugung eines gläubigen Menschen, <span style="color:#00B000">(was auch das Ziel ANSELMS war)</span>. Das Antezedens im Argument ANSELMS kann jedoch nie die persönliche Entscheidung zum Glauben, z. B. an den GOTT der Christen, im Menschen ersetzen. Das Argument ANSELMS besagt also ''':''' der Glaube eines gläubigen Menschen ist logisch widerspruchsfrei und sinnvoll, und enthält keinen Zirkelschluss, weil sein Gegenteil, der Nicht-GOTT-Glaube, zu einem Widerspruch führt; <span style="color:#00B000">(das hat GÖDEL mit seinem Kalkül-System bewiesen, dessen Argumentationskette mit einem Computer-Programm, dem sog. ,Theorembeweiser‘, überprüft worden ist, und als <span style="color:#FF6000">»''nachweisbar korrekt''«</span> befunden wurde)</span>. Das Theorem ANSELMS beweist, nach GÖDEL, dass der Glaube an GOTT, mit <span style="color:#FF6000">»''mathematische Evidenz''«</span> notwendig widerspruchsfrei und sinnvoll ist, weil der Nicht-GOTT-Glaube notwendig zu einem Widerspruch führt. <u>Das Theorem beweist jedoch nicht, dass die Existenz GOTTES notwendig ist</u>, <span style="color:#00B000">(wie es fast immer fälschlich gelesen wurde und wird; auch von A. FUHRMANN, wie auch von G. J. WIRSCHING)</span>, sondern, das Theorem ANSELMS geht davon aus, als nicht hinterfragtes Faktum des GOTT-Glaubens, dass GOTT notwendig, schon immer existiert, <span style="color:#00B000">(was mit dem 1. Hauptergebnis im 3. Beweisgang noch einmal bestätigt wird)</span>. Die ,Notwendigkeit‘ GOTTES ist immer schon eine ,vorgängig‘ entschiedene Glaubensaussage. Das GÖDEL-Kalkül beweist, <span style="color:#00B000">(mit dem Antezedens im 2. Hauptergebnis)</span>, dass diese Grundüberzeugung des GOTT-Glaubens widerspruchsfrei und sinnvoll, und <span style="color:#FF6000">»''mit allen bekannten Tatsachen durchaus vereinbar ist''«</span>, wie GÖDEL abschließend sagt. GOTT hat es ja auch nicht ,nötig‘, bewiesen zu werden. Für den Erzbischof ANSELM war die Existenz GOTTES, überall und jederzeit, die ungefragte und für ihn die selbstverständlichste Voraussetzung aller seiner Bemühungen, in seinem Leben, und in seinem Beruf als christlicher Amtsträger, und auch als <span style="color:#4C58FF">„Theologe“</span> in den philosophischen Überlegungen über seinen Glauben. Mit diesen Überlegungen hat er, mit GÖDEL, bewiesen ''':''' Glaube ist nicht irrational, kein Sprung ins Unbekannte, sondern eine freie, vernünftige Entscheidung. Glaube ist rational, und seine <span style="color:#4C58FF">„Theologie“</span> ist ,vertrauenswürdig‘.
Zusammengefasst heißt das Ergebnis dieser Bemühungen ANSELMS ''':''' Wenn Du an GOTT glauben willst, dann kannst Du das unbedenklich tun, denn Dein Glaube ist auch logisch in der <u>bewiesenen Möglichkeit</u>, dass es GOTT geben kann, begründet, und damit ist er widerspruchsfrei, sinnvoll und kein Zirkelschluss. Dein Glaube an GOTT beruht jedoch, nach wie vor und in erster Linie, auf Deiner freien Entscheidung für GOTT, und nicht auf dem Zwang einer ,logischen‘ Argumentation. Wenn Du nicht an GOTT glauben willst, dann <u>musst Du, und sollst Du, auch nicht deswegen</u> an GOTT glauben, weil der Nicht-GOTT-Glaube zu einem Widerspruch führt, und damit falsch und unlogisch ist. Denn der Glaube an GOTT muss immer eine freie und Deine ganz persönliche Entscheidung für GOTT sein und bleiben. Niemand darf zum Glauben an GOTT gezwungen werden, auch nicht mit ,logischen‘ Argumenten. Warum '''?''' Weil GOTT die Liebe ist '''!''' Und die Liebe duldet keinen Zwang '''!'''
Zum Abschluss, und für eine bessere Verständlichkeit dieser Studie, hat die KI, der ich diese Studie für die Erstellung eines Podcast übergeben habe, eine, — sogar für Kinder —, verständliche Metapher kreiert. Sie sagt, man stelle sich vor ''':''' Es sei irgendwo ein ,Schatz’ vergraben, und es gäbe auch eine ,Schatzkarte’ für diesen ,Schatz’. Kurt GÖDEL ist nun gekommen, und hat mit seinem Kalkül bewiesen, dass diese ,Schatzkarte’ korrekt ist und fehlerfrei. Ob aber jemand, mit Hilfe dieser ,Schatzkarte’ auf ,Schatzsuche’ gehen will oder nicht ''':''' das ist und bleibt jedem selbst überlassen, und ist immer eine freie, und ganz persönliche Entscheidung. Damit hat die KI erstaunlich treffend, sowohl die Bedeutung, als auch die reale Funktion des GOTT-Glaubens in der Lebens-Wirklichkeit unserer Welt und Zeit charakterisiert, in Übereinstimmung mit der Gleichniserzählung JESU vom ,Schatz im Acker’; <small><span style="color:#00B000">( vgl. {{Bibel | Matthäus-Evangelium |13|44|EU}} )</span></small>. Der ,Schatz’ ist die <span style="color:#FF6000">„<span style="font-family: Times;"><big>Βασιλεία του Θεού</big></span>“</span><span style="color:#00B000"> | ,Basileía tou Theoú’</span>, d.h. das <span style="color:#CC66FF">»''Königtum GOTTES''«</span>, oder auch das <span style="color:#CC66FF">»''Himmelreich''«</span>. Die ,Schatzkarte’ ist der Glaube an GOTT. Die ,Schatzsuche’ ist der Weg des Glaubens in der Nachfolge JESU, der uns den Weg zeigt zum Ziel, zur Erfüllung aller unserer Sehnsüchte und Wünsche ''':''' zur Teilhabe am <span style="color:#CC66FF">»''Königtum GOTTES''«</span>.
----
----
;Fußnoten
<references />
mkn3w8o3mr2vd6sxq6b9xgbr3123bbb
Ing Mathematik: Python
0
117969
1092165
1092094
2026-10-16T13:10:24Z
Intruder
1513
/* Allgemeines */
1092165
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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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}}
hw5cfwefetrmz40yszan87oq12zpua0
1092166
1092165
2026-10-16T13:31:30Z
Intruder
1513
/* if */
1092166
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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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("<=10")
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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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}}
4bksmzf0mvkg9bi13htq13lf77lr96u
1092167
1092166
2026-10-16T13:33:46Z
Intruder
1513
/* if */
1092167
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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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("<=10")
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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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}}
stwtgi1hrjag0b2ejnby5hevpchxnk1
1092168
1092167
2026-10-16T13:43:28Z
Intruder
1513
/* while */
1092168
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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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("<=10")
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 [[Ing_Mathematik:_Python#break|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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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}}
nf0n158ub0yv34euqlzq5od3p3rendi
1092170
1092168
2026-10-16T17:58:15Z
Intruder
1513
/* MS Windows */
1092170
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 (REPL ... '''R'''ead-'''E'''valuate-'''P'''rint-'''L'''oop) 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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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("<=10")
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 [[Ing_Mathematik:_Python#break|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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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}}
kd4ppf5k6nqfj7zo67kbc91bh7bswp7
1092171
1092170
2026-10-16T18:07:58Z
Intruder
1513
/* Statische Attribute, statische Methoden und Klassenmethoden */
1092171
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 (REPL ... '''R'''ead-'''E'''valuate-'''P'''rint-'''L'''oop) 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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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("<=10")
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 [[Ing_Mathematik:_Python#break|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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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. <code>@staticmethod</code> und <code>@classmethod</code> nennt man auch Dekoratoren.
= 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}}
pf4yh2yqaxdercmdubaae75686ygl8i
1092172
1092171
2026-10-16T18:44:38Z
Intruder
1513
/* Sonstiges */
1092172
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 (REPL ... '''R'''ead-'''E'''valuate-'''P'''rint-'''L'''oop) 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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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("<=10")
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 [[Ing_Mathematik:_Python#break|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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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. <code>@staticmethod</code> und <code>@classmethod</code> nennt man auch Dekoratoren.
= 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)
* MS Excel-Dateien schreiben mit dem Parameter <code>engine="openpyxl"</code>
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}}
k17uh10wpey3zmnryua2qufgj4o0xbp
1092173
1092172
2026-10-16T18:51:05Z
Intruder
1513
/* Sonstiges */ konkretisiert
1092173
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 (REPL ... '''R'''ead-'''E'''valuate-'''P'''rint-'''L'''oop) 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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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("<=10")
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 [[Ing_Mathematik:_Python#break|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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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. <code>@staticmethod</code> und <code>@classmethod</code> nennt man auch Dekoratoren.
= 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).
* MS Excel-Dateien schreiben mit dem Modul openpyxl.
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}}
5v4fy82tfigcu3c2y0nwycijrj9b0ny
1092174
1092173
2026-10-16T19:17:27Z
Intruder
1513
/* Interpolation */
1092174
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 (REPL ... '''R'''ead-'''E'''valuate-'''P'''rint-'''L'''oop) 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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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("<=10")
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 [[Ing_Mathematik:_Python#break|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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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. <code>@staticmethod</code> und <code>@classmethod</code> nennt man auch Dekoratoren.
= 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]]
<code>scipy.interpolate.interp1d()</code> gilt als veraltet ([https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html]). Stattdessen soll man modernere Alternativen wie <code>scipy.interpolate.Akima1DInterpolator()</code> oder <code>scipy.interpolate.CubicSpline()</code> benutzen.
= 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).
* MS Excel-Dateien schreiben mit dem Modul openpyxl.
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}}
mndz187nvkl0x8v2xoi76y62fyplv2q
1092176
1092174
2026-10-16T19:34:00Z
Intruder
1513
/* Interpolation */
1092176
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 (REPL ... '''R'''ead-'''E'''valuate-'''P'''rint-'''L'''oop) 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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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("<=10")
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 [[Ing_Mathematik:_Python#break|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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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. <code>@staticmethod</code> und <code>@classmethod</code> nennt man auch Dekoratoren.
= 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]]
<code>scipy.interpolate.interp1d()</code> gilt als veraltet ([https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html]). Stattdessen soll man modernere Alternativen wie <code>scipy.interpolate.Akima1DInterpolator()</code> oder <code>scipy.interpolate.CubicSpline()</code> benutzen. Für die lineare Interpolation tut es <code>np.interp()</code>.
= 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).
* MS Excel-Dateien schreiben mit dem Modul openpyxl.
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}}
5ibaj4l84ogkmz2citb5pcetb4ucmt5
1092178
1092176
2026-10-16T19:59:03Z
Intruder
1513
/* Interpolation */ Aufgaben hinzu
1092178
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 (REPL ... '''R'''ead-'''E'''valuate-'''P'''rint-'''L'''oop) 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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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("<=10")
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 [[Ing_Mathematik:_Python#break|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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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. <code>@staticmethod</code> und <code>@classmethod</code> nennt man auch Dekoratoren.
= 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]]
<code>scipy.interpolate.interp1d()</code> gilt als veraltet ([https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html]). Stattdessen soll man modernere Alternativen wie <code>scipy.interpolate.Akima1DInterpolator()</code> oder <code>scipy.interpolate.CubicSpline()</code> benutzen. Für die lineare Interpolation tut es <code>np.interp()</code>.
<u>Aufgaben:</u>
* Recherchieren Sie im Internet zu den oben genannten modernen Alternativen.
* Schreiben Sie obiges Programm auf die genannten moderneren Alternativen um.
= 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).
* MS Excel-Dateien schreiben mit dem Modul openpyxl.
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}}
0h80c5p2guoqgpzewvvkdjpwdy64qdf
1092182
1092178
2026-10-17T10:15:51Z
Intruder
1513
1092182
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 (REPL ... '''R'''ead-'''E'''valuate-'''P'''rint-'''L'''oop) 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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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("<=10")
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 [[Ing_Mathematik:_Python#break|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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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. <code>@staticmethod</code> und <code>@classmethod</code> nennt man auch Dekoratoren.
= 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.
=== Bokeh ===
Mit Bokeh kann man interaktive Datenvisualisierungen für Webbrowser in Python erstellen. Bokeh ist eine externe Bibliokek, die vorab installiert werden muss (z.B. mittel YaST oder pip).
Python-Code:
from bokeh.plotting import figure, output_file, show
x = [1, 2, 3, 4, 5, 6]
y = [10, 5, 6, 8, 9, 11]
output_file("interpolation.html")
p = figure(
title="Interpolation",
x_axis_label="x",
y_axis_label="y",
)
p.line(x, y, legend_label="Interpolierende", line_width=2, color="blue")
p.scatter(x, y, legend_label="Stützpunkte", size=7, color="orange")
p.legend.location = "top_left"
show(p)
Ausgabe:
[[File:PythonIng_bokeh1.png|300px]]
Python-Code (angelehnt an ein Beispiel aus der Bokeh-Dokumentation, aber wesentlich gekürzt
und vereinfacht):
from bokeh.layouts import layout
from bokeh.models import Spinner
from bokeh.plotting import figure, show
x = [1, 2, 3, 4, 5]
y = [2, 7, 3, 6, 4]
p = figure(x_range=(1, 5), width=500, height=350)
points = p.scatter(x=x, y=y, size=30, fill_color="blue")
spinner = Spinner(
title="Kreisgröße",
low=0,
high=60,
step=5,
value=points.glyph.size,
width=200,
)
spinner.js_link("value", points.glyph, "size")
layout = layout(
[
[spinner],
[p],
],
)
show(layout)
Ausgabe:
[[File:PythonIng_bokeh2.png|300px]]
Die Dokumentation zu Bokeh findet sich auf [https://docs.bokeh.org/en/latest/]
= 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]]
<code>scipy.interpolate.interp1d()</code> gilt als veraltet ([https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html]). Stattdessen soll man modernere Alternativen wie <code>scipy.interpolate.Akima1DInterpolator()</code> oder <code>scipy.interpolate.CubicSpline()</code> benutzen. Für die lineare Interpolation tut es <code>np.interp()</code>.
<u>Aufgaben:</u>
* Recherchieren Sie im Internet zu den oben genannten modernen Alternativen.
* Schreiben Sie obiges Programm auf die genannten moderneren Alternativen um.
= 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).
* MS Excel-Dateien schreiben mit dem Modul openpyxl.
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}}
1e4y52pi2rtveys3ow33qw8oiztzf2o
1092183
1092182
2026-10-17T10:17:50Z
Intruder
1513
/* Py5 */
1092183
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 (REPL ... '''R'''ead-'''E'''valuate-'''P'''rint-'''L'''oop) 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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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("<=10")
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 [[Ing_Mathematik:_Python#break|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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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. <code>@staticmethod</code> und <code>@classmethod</code> nennt man auch Dekoratoren.
= 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.
=== Bokeh ===
Mit Bokeh kann man interaktive Datenvisualisierungen für Webbrowser in Python erstellen. Bokeh ist eine externe Bibliokek, die vorab installiert werden muss (z.B. mittel YaST oder pip).
Python-Code:
from bokeh.plotting import figure, output_file, show
x = [1, 2, 3, 4, 5, 6]
y = [10, 5, 6, 8, 9, 11]
output_file("interpolation.html")
p = figure(
title="Interpolation",
x_axis_label="x",
y_axis_label="y",
)
p.line(x, y, legend_label="Interpolierende", line_width=2, color="blue")
p.scatter(x, y, legend_label="Stützpunkte", size=7, color="orange")
p.legend.location = "top_left"
show(p)
Ausgabe:
[[File:PythonIng_bokeh1.png|300px]]
Python-Code (angelehnt an ein Beispiel aus der Bokeh-Dokumentation, aber wesentlich gekürzt
und vereinfacht):
from bokeh.layouts import layout
from bokeh.models import Spinner
from bokeh.plotting import figure, show
x = [1, 2, 3, 4, 5]
y = [2, 7, 3, 6, 4]
p = figure(x_range=(1, 5), width=500, height=350)
points = p.scatter(x=x, y=y, size=30, fill_color="blue")
spinner = Spinner(
title="Kreisgröße",
low=0,
high=60,
step=5,
value=points.glyph.size,
width=200,
)
spinner.js_link("value", points.glyph, "size")
layout = layout(
[
[spinner],
[p],
],
)
show(layout)
Ausgabe:
[[File:PythonIng_bokeh2.png|300px]]
Die Dokumentation zu Bokeh findet sich auf [https://docs.bokeh.org/en/latest/]
= 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]]
<code>scipy.interpolate.interp1d()</code> gilt als veraltet ([https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html]). Stattdessen soll man modernere Alternativen wie <code>scipy.interpolate.Akima1DInterpolator()</code> oder <code>scipy.interpolate.CubicSpline()</code> benutzen. Für die lineare Interpolation tut es <code>np.interp()</code>.
<u>Aufgaben:</u>
* Recherchieren Sie im Internet zu den oben genannten modernen Alternativen.
* Schreiben Sie obiges Programm auf die genannten moderneren Alternativen um.
= 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).
* MS Excel-Dateien schreiben mit dem Modul openpyxl.
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}}
l51xf8ntl9n2tk7ikdpifo0u74rm6kq
1092184
1092183
2026-10-17T10:18:49Z
Intruder
1513
/* Bokeh */
1092184
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 (REPL ... '''R'''ead-'''E'''valuate-'''P'''rint-'''L'''oop) 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. 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). <code>exit</code> (ohne Klammern) oder <code>quit</code> werden auch akzeptiert.
== 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 ([[Image:Windows_logo_-_2021_(Black).svg|10px]]) 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("<=10")
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 [[Ing_Mathematik:_Python#break|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 [[Ing_Mathematik:_Python#Zugriff_auf_Matrizenelemente|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. <code>@staticmethod</code> und <code>@classmethod</code> nennt man auch Dekoratoren.
= 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.
=== Bokeh ===
Mit Bokeh kann man interaktive Datenvisualisierungen für Webbrowser in Python erstellen. Bokeh ist eine externe Bibliokek, die vorab installiert werden muss (z.B. mittel YaST oder pip).
Python-Code:
from bokeh.plotting import figure, output_file, show
x = [1, 2, 3, 4, 5, 6]
y = [10, 5, 6, 8, 9, 11]
output_file("interpolation.html")
p = figure(
title="Interpolation",
x_axis_label="x",
y_axis_label="y",
)
p.line(x, y, legend_label="Interpolierende", line_width=2, color="blue")
p.scatter(x, y, legend_label="Stützpunkte", size=7, color="orange")
p.legend.location = "top_left"
show(p)
Ausgabe:
[[File:PythonIng_bokeh1.png|300px]]
Python-Code (angelehnt an ein Beispiel aus der Bokeh-Dokumentation, aber wesentlich gekürzt
und vereinfacht):
from bokeh.layouts import layout
from bokeh.models import Spinner
from bokeh.plotting import figure, show
x = [1, 2, 3, 4, 5]
y = [2, 7, 3, 6, 4]
p = figure(x_range=(1, 5), width=500, height=350)
points = p.scatter(x=x, y=y, size=30, fill_color="blue")
spinner = Spinner(
title="Kreisgröße",
low=0,
high=60,
step=5,
value=points.glyph.size,
width=200,
)
spinner.js_link("value", points.glyph, "size")
layout = layout(
[
[spinner],
[p],
],
)
show(layout)
Ausgabe:
[[File:PythonIng_bokeh2.png|300px]]
Die Dokumentation zu Bokeh findet sich auf [https://docs.bokeh.org/en/latest/].
Hiermit belassen wir es mit den Grafikbibliotheken 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]]
<code>scipy.interpolate.interp1d()</code> gilt als veraltet ([https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html]). Stattdessen soll man modernere Alternativen wie <code>scipy.interpolate.Akima1DInterpolator()</code> oder <code>scipy.interpolate.CubicSpline()</code> benutzen. Für die lineare Interpolation tut es <code>np.interp()</code>.
<u>Aufgaben:</u>
* Recherchieren Sie im Internet zu den oben genannten modernen Alternativen.
* Schreiben Sie obiges Programm auf die genannten moderneren Alternativen um.
= 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).
* MS Excel-Dateien schreiben mit dem Modul openpyxl.
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}}
i03351cd9pjxr9e1cpatel15kjsmgvs
Stenografie/ Kapitel 7
0
120059
1092175
1044998
2026-10-16T19:21:32Z
~2026-55371-50
117057
1092175
wikitext
text/x-wiki
[[Stenografie/ Kapitel 6|Kapitel 6]] ← '''Kapitel 7''' → [[Stenografie/ Kapitel 8|Kapitel 8]]
== k, ck ==
:Das Zeichen für „k“ ist halbstufig. Es ähnelt dem oberen Teil eines Kreises. Das gleiche Zeichen steht auch für „ck“. (c = ck)
:Wenn das "k" auf einen Selbstlaut folgt erhält es einen schrägen Anstrich.
:{| class="wikitable" style="margin: 1em auto 1em auto; margin-left: 10px"
|-align="center"
| bgcolor="#ff88ff " | [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - k.svg|thumb| Buchstabe: k ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Kette.svg|thumb| {{Antwort1|Kette}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - nette.svg|thumb| {{Antwort1|nette}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - der Kenner.svg|thumb|{{Antwort1|der Kenner}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - kennen.svg|thumb| {{Antwort1|kennen}} ]] || [[File:B DEK Deutsche Einheitskurzschrift - Verkehrsschrift - die Motte.svg|thumb|{{Antwort1|die Motte}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - necken.svg|thumb| {{Antwort1|necken}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - kommen.svg|thumb|{{Antwort1|kommen}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - komm.svg|thumb| {{Antwort1|komm}} ]] || [[File:B DEK Deutsche Einheitskurzschrift - Verkehrsschrift - ich komme.svg|thumb|{{Antwort1|ich komme}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Becken.svg|thumb| {{Antwort1|Becken}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Ketten.svg|thumb| {{Antwort1|Ketten}} ]]
|}
== „ek-“ ==
:Ein "e" vor dem "k" wird durch einen verlängerten Anstrich des "k" versinnbildlicht.
:{| class="wikitable" style="margin: 1em auto 1em auto; margin-left: 10px"
|-align="center"
| [[File:B DEK Deutsche Einheitskurzschrift - Verkehrsschrift - die Ecke.svg|thumb| {{Antwort1|die Ecke}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Ecken.svg|thumb|{{Antwort1|Ecken}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Otto ist keck.svg|thumb| {{Antwort1|Otto ist keck}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - toben.svg|thumb|{{Antwort1|toben}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - mehr Rohre.svg|thumb| {{Antwort1|mehr Rohre}} ]] || [[File:G DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Rom.svg|thumb|{{Antwort1|Rom}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - der Meter.svg|thumb| {{Antwort1|der Meter}} ]] || [[File:B DEK Deutsche Einheitskurzschrift - Verkehrsschrift - das Meter.svg|thumb|{{Antwort1|das Meter}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Motor.svg|thumb| {{Antwort1|Motor}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Rad.svg|thumb|{{Antwort1|Rad}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Rat.svg|thumb| {{Antwort1|Rat}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Motorrad.svg|thumb|{{Antwort1|Motorrad}} ]]
|}
== 015 ==
:{| class="wikitable" style="margin: 1em auto 1em auto; margin-left: 10px"
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - nehmen.svg|thumb| {{Antwort1|nehmen}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - neben.svg|thumb|{{Antwort1|neben}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - eben.svg|thumb| {{Antwort1|eben}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Theben.svg|thumb|{{Antwort1|Theben}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - toben.svg|thumb| {{Antwort1|toben}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - monoton.svg|thumb|{{Antwort1|monoton}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - nennen.svg|thumb| {{Antwort1|nennen}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - beben.svg|thumb|{{Antwort1|beben}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - boten.svg|thumb| {{Antwort1|boten}} oder {{Antwort1|Boten}} oder {{Antwort1|Booten}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - bergen.svg|thumb|{{Antwort1|bergen}} ]]
|-align="center"
| [[File:B DEK Deutsche Einheitskurzschrift - Verkehrsschrift - der Berg.svg|thumb| {{Antwort1|der Berg}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - borgen.svg|thumb|{{Antwort1|borgen}} ]]
|}
== f ==
:Das Zeichen für „f“ ist zweistufig, ähnelt aber ansonsten dem „d“
:{| class="wikitable" style="margin: 1em auto 1em auto; margin-left: 10px"
|-align="center"
| bgcolor="#ff88ff " | [[File:K DEK Deutsche Einheitskurzschrift - Verkehrsschrift - f.svg|thumb| Buchstabe: f ]] || bgcolor="#ffddff" | [[File:K DEK Deutsche Einheitskurzschrift - Verkehrsschrift - d.svg|thumb| Buchstabe: d ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - fegen.svg|thumb|{{Antwort1|fegen}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - kommen.svg|thumb|{{Antwort1|fegen}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Ocker.svg|thumb| {{Antwort1|Ocker}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Ecke.svg|thumb|{{Antwort1|Ecke}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Decke.svg|thumb| {{Antwort1|Decke}} ]] || [[File:B DEK Deutsche Einheitskurzschrift - Verkehrsschrift - die Feder.svg|thumb|{{Antwort1|die Feder}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - reden.svg|thumb| {{Antwort1|reden}} ]] || [[File:B DEK Deutsche Einheitskurzschrift - Verkehrsschrift - der Koffer.svg|thumb|{{Antwort1|der Koffer}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Rotton.svg|thumb| {{Antwort1|Rotton}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - oder.svg|thumb|{{Antwort1|oder}} ]]
|}
== für ==
:Für das Kürzel „für“ verwenden wir das gleiche Zeichen wie für den Buchstaben „f“.
:{| class="wikitable" style="margin: 1em auto 1em auto; margin-left: 10px"
|-align="center"
| bgcolor="#ff88ff " | [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - für.svg|thumb| Kürzel: für ]] || [[File:P DEK Deutsche Einheitskurzschrift - Verkehrsschrift - für Otto.svg|thumb| {{Antwort1|für Otto}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - mohnrot.svg|thumb| {{Antwort1|mohnrot}} ]] || [[File:z DEK Deutsche Einheitskurzschrift - Verkehrsschrift - ist das für Benno.svg|thumb|{{Antwort1|ist das für Benno}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - roter Mohn.svg|thumb| {{Antwort1|roter Mohn}} ]] || [[File:B DEK Deutsche Einheitskurzschrift - Verkehrsschrift - mehr Regen.svg|thumb|{{Antwort1|mehr Regen}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - begeben.svg|thumb| {{Antwort1|begeben}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - begonnen.svg|thumb|{{Antwort1|begonnen}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - benehmen.svg|thumb| {{Antwort1|benehmen}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - benommen.svg|thumb|{{Antwort1|benommen}} ]]
|-align="center"
| [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - Roboter.svg|thumb| {{Antwort1|Roboter}} ]] || [[File:DEK Deutsche Einheitskurzschrift - Verkehrsschrift - betonen.svg|thumb|{{Antwort1|betonen}} ]]
|}
[[Stenografie/ Kapitel 6|Kapitel 6]] ← '''Kapitel 7''' → [[Stenografie/ Kapitel 8|Kapitel 8]]
b23j42jav9jke1e6gzz5q3e7cih8htm
Diskussion:Stenografie/ Kapitel 7
1
123175
1092177
2026-10-16T19:47:07Z
~2026-55371-50
117057
Neuer Abschnitt /* Fehler bei "ek"? */
1092177
wikitext
text/x-wiki
== Fehler bei "ek"? ==
Das letzte Wort des "ek" Teils soll "Motorrad" sein, die Buchstaben ergeben jedoch "Motorerad". Ich weiß leider nicht wie das zu korrigieren ist. [[Spezial:Beiträge/~2026-55371-50|~2026-55371-50]] ([[Benutzer Diskussion:~2026-55371-50|Diskussion]]) 21:47, 16. Okt. 2026 (CEST)
7vacujh4gbc81n15m2grlhlm64hixm4