1. Erste Schritte#

Wir wollen uns zunächst mit den Grundprinzipien der Programmiersprache Python vertraut machen. Insbesondere wollen wir lernen, welche Datentypen von Haus aus bereitgestellt werden, und wie wir mit diesen Arbeiten. Ferner wollen wir uns schon ein bisschen an die Syntax dieser Programmiersprache gewöhnen.

1.1. Variablen und Datentypen#

1.1.1. Python als Taschenrechner verwenden#

Python lässt sich wie ein Taschenrechner verwenden. Zwischenergebnisse können mit dem Zuweisungsoperator = ganz einfach in Variablen abgespeichert werden:

3+5
8
a = 3+5
b = a/2
b
4.0

Mit der type-Funktion lässt sich der Datentyp der Variable ermitteln:

print("a  ist ein", type(a))
print("2  ist ein", type(2))
print("2. ist ein", type(2.))
print("b  ist ein", type (b))
a  ist ein <class 'int'>
2  ist ein <class 'int'>
2. ist ein <class 'float'>
b  ist ein <class 'float'>

Weitere mathematische Funktionen sind im Modul math enthalten, welches zunächst in das Python-Skript eingebunden werden muss:

import math

print("Die Wurzel aus 9 ist", math.sqrt(9))
print("2 hoch 3 ist", 2**3)
print("Der natürliche Logarithmus aus 3 ist", math.log(3))
Die Wurzel aus 9 ist 3.0
2 hoch 3 ist 8
Der natürliche Logarithmus aus 3 ist 1.0986122886681098

Um zu erfahren welche Funktionen die math-Bibliothek noch bereitstellt kann man

math.<TAB>

eingeben

Übungsaufgabe

Ermitteln Sie die Werte von \(\sin(x)\) für \(x\in \{0,\pi/2, \pi, 3\pi/2, 2\pi\}\).

In diesem Beispiel haben wir auch gesehen, wie zusätzliche Module eingebunden und verwendet werden. Funktionen aus dem Modul math müssen hier mit math.sin(...) angesprochen werden. Alternativ kann man alle Funktionen einer Bibliothek mittels

from math import *

einbinden und alle Funktionen und Variablen ohne Angabe des Bibliotheksnamens ansprechen.

Achtung: Hierbei können Namenskonflikte mit Funktionen und Variablen anderer Bibliotheken entstehen.

from math import *

print("Der Sinus von pi/2 ist", sin(pi/2))
Der Sinus von pi/2 ist 1.0

1.1.2. Numerische Datentypen#

Wir haben im vorherigen Abschnitt bereits die type-Funktion kennengelernt und haben 2 verschiedene Datentypen bei unseren Berechnungen beobachtet. Die numerischen Datentypen sind in folgender Tabelle zusammengefasst:

Datentyp

Beschreibung

Beispiele

int

Ganze Zahlen

2, 3*8

float

Fließkommazahlen

2., 3/2, math.pi

bool

Bool’scher Wert

True, 1==0, 1<3

complex

Komplexe Zahlen

2+4j, cmath.sqrt(-9)

Boolsche Variablen nehmen lediglich die Werte wahr (True) oder falsch (False) an. Sie sind unter Anderem das Ergebnis von Vergleichsoperationen:

a = 1 < 3
print("a ist eine Variable vom Typ", type(a), "und hat den Wert", a)
b = 1==0
print("b ist eine Variable vom Typ", type(b), "und hat den Wert", b)
a ist eine Variable vom Typ <class 'bool'> und hat den Wert True
b ist eine Variable vom Typ <class 'bool'> und hat den Wert False

Für komplexe Zahlen sind entsprechende Rechenoperationen in der Bibliothek cmath definiert, die wir über

import cmath

einbinden können. Eine kurze Dokumentation zu einer Bibliothek bekommt man übrigens mit

cmath?

Hier ein kleines Beispiel:

import cmath

a = cmath.sqrt(-9)
print("Die Wurzel aus -9 ist", a)

b = 1+2j
c = a/b

print("Der Quotient aus a und b ist", c, "und ist wieder vom Typ", type(c))
print("Es gilt Re(c) =", c.real, 
      ", Im(c) =", c.imag, 
      "und die konjugiert Komplexe ist =", c.conjugate())
Die Wurzel aus -9 ist 3j
Der Quotient aus a und b ist (1.2+0.6j) und ist wieder vom Typ <class 'complex'>
Es gilt Re(c) = 1.2 , Im(c) = 0.6 und die konjugiert Komplexe ist = (1.2-0.6j)

Übungsaufgabe

Berechne den Betrag und das Argument der Zahl 1+i.

Hinweis: Schaue dir zunächst an, welche Funktionen cmath bereitstellt.

1.1.3. Strings#

Datentyp

Beschreibung

Beispiel

string

Eine beliebig lange Zeichenkette

'Hello world', "ABCDE"

Ein Objekt vom Typ string ist eine Zeichenkette, wie wir Sie bei den Konsolenausgaben vorher schon verwendet haben. Strings werden durch Apostrophen oder Gänsefüßchen begrenzt.

a = "Ich bin ein String."                # String erzeugen
a+= "\nEin sehr langer String."          # Einen weiteren String anhängen
a+= "\nNun schon "+str(3)+" Zeilen lang" # Integer in String verwandeln und anhängen

print(a)
Ich bin ein String.
Ein sehr langer String.
Nun schon 3 Zeilen lang

Das Sonderzeichen '\n' steht dabei für einen Zeilenumbruch. Um Objekte anderer Typen in einen string umzuwandeln, nutzen wir die Funktion str(). Der Additionsoperator + verknüpft 2 Strings miteinander und der Operator += hängt einen String an einen anderen an.

Geben wir a.<TAB> ein, bekommen wir wieder eine Liste mit nützlichen Funktionen, die wir auf Strings anwenden können. Alternativ lassen sich alle für Strings definierten Methoden wie folgt auflisten:

dir(a)
['__add__',
 '__class__',
 '__contains__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__getnewargs__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__len__',
 '__lt__',
 '__mod__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__rmod__',
 '__rmul__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'capitalize',
 'casefold',
 'center',
 'count',
 'encode',
 'endswith',
 'expandtabs',
 'find',
 'format',
 'format_map',
 'index',
 'isalnum',
 'isalpha',
 'isascii',
 'isdecimal',
 'isdigit',
 'isidentifier',
 'islower',
 'isnumeric',
 'isprintable',
 'isspace',
 'istitle',
 'isupper',
 'join',
 'ljust',
 'lower',
 'lstrip',
 'maketrans',
 'partition',
 'removeprefix',
 'removesuffix',
 'replace',
 'rfind',
 'rindex',
 'rjust',
 'rpartition',
 'rsplit',
 'rstrip',
 'split',
 'splitlines',
 'startswith',
 'strip',
 'swapcase',
 'title',
 'translate',
 'upper',
 'zfill']

Diese Liste enthält die Namen aller Methoden, die wir auf das Objekt a anwenden können. Eine Methode ist im Allgemeinen eine Funktionen, die auf Objekte eines speziellen Datentyps (in unserem Beispiel string) angewendet werden können. Der Aufruf einer Methode sieht wie folgt aus:

<result> = <object>.<method>(<param1>, <param2>, ...)

Dabei ist <object> der Name eines Python-Objektes (in unserem Fall a, welches vom Typ stringist), <method> der Name einer Methode aus der oben erzeugten Liste und <result> das Objekt, was diese Methode zurückgibt. Es gibt allerdings auch Methoden, die nichts zurückgeben. In diesem Fall entfällt dieser Teil. Testen wir dies mit der Methode replace, welche - wie der Name bereits vermuten lässt - bestimmte Zeichen(ketten) durch andere ersetzt. Die offensichtliche Frage für uns Programmierer/innen ist nun:

Welche Parameter muss ich der Methode übergeben und was gibt diese Methode zurück?

Was sind also <param1> etc. und <result>?

Hierbei sei angemerkt, dass man nicht unbedingt ein/e gute/r Programmierer/in ist, wenn man für möglichst viele Datentypen die Methoden und ihre Parameter auswendig gelernt hat. Viel sinnvoller hingegen ist es zu lernen Code-Dokumentationen zu lesen. Geben wir doch einfach mal python string replace bei Google ein und öffnen den ersten Treffer: https://www.w3schools.com/python/ref_string_replace.asp. Diese Dokumentation enthält alles, was wir über diese Methode wissen müssen:

  • 2 Parameter werden benötigt: ** oldvalue - der Teil des Strings, der ersetzt werden soll ** newvalue - eine neue Zeichenkette die anstelle von oldvalue in unserem String stehen soll

  • Der dritte Parameter count ist optional. Mit diesem können wir einstellen, wie viele der Teilstrings ersetzt werden sollen. Die Standardeinstellung ist: ersetze Alle.

  • Der Rückgabeparameter ist ein neuer String, der aus der replace-Methode hervorgeht.

Alternativ zur Google-Suche könnten wir auch mit der im Jupyter-Notebook eingebauten Code-Dokumentation arbeiten. Gibt man zum Beispiel

a.replace?

ein, dann öffnet sich folgende Dokumentation:

Signature: a.replace(old, new, count=-1, /)
Docstring:
Return a copy with all occurrences of substring old replaced by new.

  count
    Maximum number of occurrences to replace.
    -1 (the default value) means replace all occurrences.

If the optional argument count is given, only the first count occurrences are
replaced.
Type:      builtin_function_or_method

Die Anwendung der replace-Methode für Strings sieht beispielsweise wie folgt aus:

# String definieren
a = "Ich bin ein String"

# Bestimmte Zeichenfolgen ersetzen
a = a.replace("Ich", "I")
a = a.replace("bin", "bims")
a = a.replace("ein", "1")

# Konsolenausgabe
print(a)
I bims 1 String

Wenn wir uns nochmals die Liste aller Methoden für Strings von oben anschauen, dann fällt uns auf, dass einige Methoden mit 2 Unterstrichen beginnen. Dies sind bewusst versteckte Methoden und sollten von der Programmiererin nicht direkt verwendet werden. Einige dieser Methoden dienen zur Überladung von Operatoren, beispielsweise realisiert die Methode __eq__ den Vergleichsoperator ==, oder __add__ den Additionsoperator +. Wir diskutieren dies noch einmal genauer im Abschnitt Spezielle Methoden.

In folgendem Beispiel wenden wir einige der für Strings definierten Methoden an:

# Verknüpfungsoperator += (ruft intern __add__ auf)
a = "Ich bin ein String,"
a+= " der schon recht lang ist"
print(a)

# Vergleichsoperator == (ruft intern __eq__ auf)
b = "abc"
v = (b == "abc") 
print("Die Aussage 'b ist gleich abc' ist", v)
Ich bin ein String, der schon recht lang ist
Die Aussage 'b ist gleich abc' ist True

Übungsaufgabe

Definiere einen String mit Inhalt “Ich bin ein sehr langer String”. Zähle die Anzahl der Buchstaben und zerlege den String in seine einzelnen Wörter.

Hinweis: Wir wollen die Methoden count und split ausprobieren. Nutze eine Code-Dokumentation um herauszufinden, wie diese Methoden anzuwenden sind.

Die Methode string.split() erzeugt ein Objekt vom Typ list, welche wir im folgenden Abschnitt genauer diskutieren.

1.1.4. Containerklassen#

Weitere wichtige Datentypen sind Listen und n-Tupel:

Datentyp

Beschreibung

Beispiele

list

Liste beliebig vieler Elemente beliebigen Typs

[1,2.2,'Hallo']

tuple

Ansammlung einer fixen Anzahl an Elementen

(1,"a")

Weitere Containerklassen sind set und dictionary, welche wir hier noch nicht behandeln möchten.

list ist ein Datentyp, welcher es erlaubt mehrere Objekte beliebigen Typs in einem Objekt zusammenzufassen. Ferner werden nützliche Methoden bereitgestellt um auf die Liste zuzugreifen oder diese zu manipulieren:

dir(list)
['__add__',
 '__class__',
 '__class_getitem__',
 '__contains__',
 '__delattr__',
 '__delitem__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__gt__',
 '__hash__',
 '__iadd__',
 '__imul__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__len__',
 '__lt__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__reversed__',
 '__rmul__',
 '__setattr__',
 '__setitem__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'append',
 'clear',
 'copy',
 'count',
 'extend',
 'index',
 'insert',
 'pop',
 'remove',
 'reverse',
 'sort']
L = [1,3,5]        # Liste erzeugen              [1,3,5]
L.append(2)        # Element anhängen            [1,3,5,2]
L = L + [4,6]      # Eine weitere Liste anhängen [1,3,5,2,4,6] 
L.sort()           # Liste sortieren             [1,2,3,4,5,6]
L.pop()            # Letztes Element entfernen   [1,2,3,4,5]
L                  # Konsolenausgabe
[1, 2, 3, 4, 5]

Interessant ist die Beobachtung, dass der Additionsoperator + auch für 2 Objekte vom Typ list definiert ist. Das Ergebnis ist offensichtlich die Konkatenation beider Listen.

Auch um Listen zu erstellen gibt es mehrere Möglichkeiten. Nützlich ist hierbei die Klasse range (siehe range? für eine genaue Beschreibung). Einige Beispiele:

L1 = list(range(10)) # Äquivalent zu L1 = [0,1,2,...,9]
print("L1 =", L1, ": enthält", len(L1), "Elemente")
L1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] : enthält 10 Elemente
L2 = list(range(1,11,2)) # Ungerade Zahlen von 1 bis 10
print("L2 =", L2, ": enthält", len(L2), "Elemente")
L2 = [1, 3, 5, 7, 9] : enthält 5 Elemente

Mit einer for-Schleife können wir über eine Liste iterieren und auf einzelne Elemente zugreifen. Die Syntax einer solchen Schleife sieht wie folgt aus:

for [elem] in [liste]:
    [do something]

Ein einfaches Beispiel:

for a in L:
    print("Die Liste enthält", a)
Die Liste enthält 1
Die Liste enthält 2
Die Liste enthält 3
Die Liste enthält 4
Die Liste enthält 5

Übungsaufgabe

Berechne die Summe der Zahlen 1 bis 10. Nutze entweder eine for-Schleife oder die Funktion sum(...).

Um ein einzelnes Element einer Liste zu lesen oder zu schreiben nutzt man den Zugriffsoperator []. Beachte, dass das erste Element den Index 0 besitzt, das zweite den Index 1, etc..

import random # Zufallszahlengenerator

farben = ['rot', 'blau', 'grün', 'schwarz', 'gelb']

for i in range(3):
    index = random.randint(0, len(farben)-1) # Erzeuge Zufallszahl zwischen 0 und 4
    farbe = farben[index]                    # Wähle die entsprechende Farbe
    print("Ich seh' etwas, was du nicht siehst, und das ist", farbe)
Ich seh' etwas, was du nicht siehst, und das ist gelb
Ich seh' etwas, was du nicht siehst, und das ist grün
Ich seh' etwas, was du nicht siehst, und das ist schwarz

Zuletzt untersuchen wir die Klasse tuple. Diesen Container verwendet man für geordnete Listen einer festen Größe. Diese finden beispielsweise Einsatz bei Funktionen, welche 2 oder mehrere Werte zurückgeben. Diese Werte werden dann als n-Tupel zurückgegeben. Ein Beispiel ist der Betrag \(r\) und das Argument \(\varphi\) von komplexen Zahlen \(z = a+i\,b = r\,e^{i\,\varphi}\):

import cmath

a = 1+3j
res = cmath.polar(a)
print("Der Rückgabewert", res, "der Funktion 'cmath.polar' ist vom Typ", type(res))
Der Rückgabewert (3.1622776601683795, 1.2490457723982544) der Funktion 'cmath.polar' ist vom Typ <class 'tuple'>
print("Betrag   :", res[0])
print("Argument :", res[1])
Betrag   : 3.1622776601683795
Argument : 1.2490457723982544

Der Zugriff auf ein Element des Tupels erfolg wieder mit dem Operator [].

Noch eleganter ist allerdings folgende Schreibweise:

(a_abs, a_arg) = cmath.polar(a)

print("Betrag   :", a_abs)
print("Argument :", a_arg)
Betrag   : 3.1622776601683795
Argument : 1.2490457723982544

1.2. Klassen und Funktionen#

In den letzten Abschnitten haben wir schon einige wichtige Konzepte der Programmiersprache Python kennengelernt. Diese wollen wir hier nochmal zusammenfassen.

  • Variablen

Hinter jeder Variablen verbirgt sich ein Objekt, auch Instanz genannt. Jede Instanz, gehört einer Klasse an, beispielsweise gehört 3.0 zur Klasse float, "Hallo" zur Klasse string und [1,2,3] zur Klasse list. Zu welcher Klasse eine Instanz gehört erfahren wir dabei mit der Funktion type(...). Wir werden im Laufe dieser Vorlesung noch viele weitere Klassen kennenlernen und sogar eigene erstellen. Wichtig ist an dieser Stelle zu wissen, dass Klassen spezifische Attribute und Operationen für Instanzen dieser Klasse zusammenfassen. Beispielsweise speichert die Klasse complex den Real- und Imaginärteil einer komplexen Zahl, auf die wir mit

a.real
1.0
a.imag
3.0

zugreifen.

  • Methoden

Methoden sind Funktionen, welche an Instanzen einer Klasse gekoppelt sind. Diese lassen sich mit

<result> = <instance>.<method>(<param1>, <param2> [...])

aufrufen.

Die Dokumentation zu einer Methode erhalten wir mit

<instance>.<method>?

Um zu erfahren welche Methoden eine Klasse anbietet, erzeugen wir eine Instanz a dieser Klasse und tippen a.<TAB> ins Notebook ein, oder führen die Funktion dir(a) aus. Die Klasse complex stellt beispielsweise die Funktion conjugate() bereit und kann wie folgt aufgerufen werden:

b = a.conjugate()
print("Die konjugiert komplexe Zahl von", a, "ist", b)
Die konjugiert komplexe Zahl von (1+3j) ist (1-3j)
  • Freie Funktionen

Freie Funktionen hingegen sind nicht an eine Klasse gekoppelt, sondern lassen sich ohne Angabe der Instanz aufrufen. Ein Beispiel ist die Funktion sum(...). Oft lassen sich diese Funktionen auf Objekte von unterschiedlichen Klassen anwenden. So funktioniert die Funktion sum(...) für Listen

sum([1,2,3])
6

als auch für Tupel

sum((1,2,3))
6

Für Strings hingegen ist die Funktion sum nicht anwendbar und gibt einen Fehler aus:

sum("Teststring")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[26], line 1
----> 1 sum("Teststring")

TypeError: unsupported operand type(s) for +: 'int' and 'str'