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.
Variablen und Datentypen¶
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+58a = 3+5
b = a/2
b4.0Variablen müssen in Python nicht vorher deklariert werden. Der Datentyp wird automatisch anhand des zugewiesenen Wertes bestimmt.
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 Funktion das math-Modul noch bereitstellt, kann man im JupyterLab einfach
math.<TAB>eingeben. Daraufhin erscheint eine Liste aller verfügbaren Funktionen und Konstanten des Moduls.
In diesem Beispiel haben wir auch gesehen, wie zusätzliche Module importiert 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 *importieren und alle Funktionen und Variablen ohne Angabe des Modulnamens 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
Numerische Datentypen¶
Wir haben im vorherigen Abschnitt bereits die type-Funktion kennengelernt und haben zwei 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
Komplexe Zahlen sind in Python ein eingebauter Datentyp (complex).
Die imaginäre Einheit wird dabei mit j statt mit i geschrieben.
Für komplexe Zahlen sind entsprechende Rechenoperationen im Modul cmath definiert, das wir über
import cmatheinbinden können. Eine kurze Dokumentation zu einer Bibliothek bekommt man übrigens, wenn man
cmath?in eine Code-Zelle eingibt.
Hier ein kleines Beispiel zu komplexen Zahlen:
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)
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 Apostrophe 'oder Anführungszeichen " 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__',
'__getstate__',
'__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 bestimmten Datentyps (in unserem Beispiel string) angewendet werden kann. Der Aufruf einer Methode sieht folgendermaßen aus:
<result> = <object>.<method>(<param1>, <param2>, ...)<object>ist der Name eines Python-Objekts (hiera, vom Typstring).<method>ist der Name einer Methode aus der oben erzeugten Liste.<result>ist das Objekt, das von der Methode zurückgegeben wird.
Es gibt allerdings auch Methoden, die nichts zurückgeben. In diesem Fall entfällt der Teil <result> =.
Testen wir dies mit der Methode replace, welche - wie der Name bereits vermuten lässt - bestimmte Zeichen(ketten) durch andere ersetzt. Die zentrale Frage lautet:
Welche Parameter muss ich der Methode übergeben und was gibt diese Methode zurück?
Dabei sei angemerkt: Ein guter Programmierer ist nicht derjenige, der sich Methoden und Parameter aller Datentypen auswendig merkt. Viel wichtiger ist es, Code-Dokumentationen lesen zu können.
Beispiel: Google nach python string replace und öffne den ersten Treffer: https://
Zwei Parameter werden benötigt: **
oldvalue- der Teil des Strings, der ersetzt werden soll **newvalue- die neue Zeichenkette, die anstelle vonoldvalueeingefügt wirdEin optionaler Parameter
countlegt fest, wie viele Vorkommen ersetzt werden sollen. Standardmäßig werden alle ersetzt.Die Methode gibt einen neuen String zurück, der die Änderungen enthält.
Alternativ kann man die im JupyterLab eingebaute Dokumentation verwenden:
a.replace?Das liefert z.B.:
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_methodDie 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, fällt auf, dass einige Methoden mit zwei Unterstrichen beginnen. Dies sind bewusst versteckte Methoden und sollten in der Regel nicht direkt verwendet werden.
Einige dieser Methoden dienen zur Überladung von Operatoren, z.B.:
__eq__→ realisiert den Vergleichsoperator==__add__→ realisiert 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
Die Methode string.split() erzeugt ein Objekt vom Typ list, welchen wir im folgenden Abschnitt genauer diskutieren werden.
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 festen Anzahl von Elementen | (1,"a") |
set | Menge von einzigartigen Elementen | {1, 2, 3} |
dict | Schlüssel-Wert-Paare | {'Name': 'Max', 'Alter': 25} |
Listen¶
Wir schauen uns zunächst Listen genauer an.
list ist ein Datentyp, der es erlaubt, mehrere Objekte beliebigen Typs in einem einzigen Objekt zusammenzufassen. Außerdem werden zahlreiche nützliche Methoden bereitgestellt, um auf die Elemente der Liste zuzugreifen oder die Liste zu verändern.
dir(list)['__add__',
'__class__',
'__class_getitem__',
'__contains__',
'__delattr__',
'__delitem__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',
'__getstate__',
'__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']Wir sehen, dass der Datentyp list Methoden wie append, clear, copy usw. bereitstellt.
Außerdem sehen wir, dass auch ein Additionsoperator (über __add__), ein Vergleichsoperator (über __le__) und ein Multiplikationsoperator (über __mul__) definiert sind.
Probieren wir einige dieser Methoden aus:
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 = 2 * L # Liste verdoppeln [1,2,3,4,5,1,2,3,4,5]
L # Konsolenausgabe[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]Auch zum Erzeugen von Listen 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 [element] 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
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
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 rot
Ich seh' etwas, was du nicht siehst, und das ist gelb
Ich seh' etwas, was du nicht siehst, und das ist blau
Tupel¶
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 und das Argument von komplexen Zahlen :
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 erfolgt wieder mit dem Operator [].
Noch eleganter ist allerdings Tuple-Unpacking:
(a_abs, a_arg) = cmath.polar(a)
print("Betrag :", a_abs)
print("Argument :", a_arg)Betrag : 3.1622776601683795
Argument : 1.2490457723982544
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.0zur Klassefloat"Hallo"zur Klassestring[1,2,3]zur Klasselist
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.
Beispiel: Die Klasse complex speichert den Real- und Imaginärteil einer komplexen Zahl. Darauf können wir zugreifen mit:
a.real1.0a.imag3.0Methoden
Methoden sind Funktionen, welche an Instanzen einer Klasse gekoppelt sind. Diese lassen sich mit
<result> = <instance>.<method>(<param1>, <param2> [...])aufrufen. Methoden, die keine Eingabeparameter benötigen, werden dennoch mit leeren Klammern () aufgerufen.
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.
Beispiel: Die Klasse complex stellt 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 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])6als auch für Tupel
sum((1,2,3))6Für Strings hingegen ist die Funktion sum nicht anwendbar und gibt einen Fehler aus:
For strings, however, the function sum is not applicable and produces an error:
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'