6 Grundlegende Sprachelemente

6.1 Syntax und Semantik

Zur Syntax mit der Einrückung …

[1]:
variable = 666
if variable == 666:
    print('jo super')
    print('noch besser')
jo super
noch besser

In C zum Beispiel geht das so (wobei hier die Einrückung irrelevant ist):

void f() {
    int variable = 666;
    /* ... */
}

In Python können Variablen ihren Typ ändern

[2]:
variable = 666
print(type(variable))
variable = 3.14
print(type(variable))
variable = 'Ein depperter String'
print(type(variable))
<class 'int'>
<class 'float'>
<class 'str'>

Sidestep: selbst Funktionen sind Objekte, halt eben vom Typ “Function” und nicht z.B. int

[3]:
type(print)
[3]:
builtin_function_or_method

print ist nur ein Name für ein Funktionsobjekt. Eine Variable sozusagen. Diese kann man genauso zuweisen und verwenden (aufrufen, in diesem Fall).

[4]:
variable = print
variable('Hello World!')
Hello World!

Kommentare

[5]:
zaehler = 0
print('initialwert ist', zaehler)
# jetzt zaehlen wir von 0 bis 9
while zaehler < 10:
    print(zaehler)
    # hier bereiten wir den naechsten schleifendurchlauf vor
    zaehler = zaehler + 1    # oder auch: zaehler += 1
print('fertig')
initialwert ist 0
0
1
2
3
4
5
6
7
8
9
fertig

6.2 Grundlegende Elemente einer Sprache

6.3 Standarddatentypen (elementare Datentypen)

Integers sind beliebig groß

Dieser hier passt locker in ein 64 Bit CPU Register

[6]:
mein_integer = 2**10
print(mein_integer)
1024

Dieser hier gerade noch. Um eins mehr und er wäre dort wieder 0.

[7]:
2**64-1
[7]:
18446744073709551615

Python schützt mich vor solchen Overflow-Bugs, indem Zahlen, die nicht in die CPU passen, in Software gehandhabt werden.

[8]:
2**64
[8]:
18446744073709551616
[9]:
2**100
[9]:
1267650600228229401496703205376

2 hoch 1000 passt in keine CPU.

[10]:
2**1000
[10]:
10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376

Vergleichsoperatoren

[11]:
1 == 2
[11]:
False
[12]:
42 == 42
[12]:
True
[13]:
if 42 == 42:
    print('eh klar')
eh klar
[14]:
1 < 2
[14]:
True
[15]:
1 <= 2
[15]:
True
[16]:
1 != 2
[16]:
True

Operator Präzedenz: Punkt vor Strichrechnung

[17]:
1 + 2 * 3
[17]:
7
[18]:
(1 + 2) * 3
[18]:
9

Floor Division

[19]:
1/2
[19]:
0.5
[20]:
3/2
[20]:
1.5

In C ist aber 3/2 == 1 !!! Rechnen tut dort ausschliesslich die CPU, und 3/2 ist eine Ganzzahldivision mit einem ganzzahligen Resultat.

[21]:
3//2
[21]:
1

Modulo Operator: %

Rest einer ganzzahligen Division

[22]:
9%2
[22]:
1
[23]:
11%2
[23]:
1
[24]:
9%3
[24]:
0
[25]:
9%5
[25]:
4
[26]:
variable = 42
if variable % 2 == 0:
    print(variable, 'ist gerade')
42 ist gerade

(Unärer) Negations Operator

[27]:
variable = 42
variable = -variable
[28]:
variable
[28]:
-42

Operator Präzedenz erzwingen

  • und % haben gleichen Vorrang

[29]:
2 * 9 % 5
[29]:
3
[30]:
2 * (9 % 5)
[30]:
8
[31]:
(2 * 9) % 5
[31]:
3

Strings gibts auch (Zeichenketten)

[32]:
s = "abc"
print(s)
abc
[33]:
s = 'abc'
print(s)
abc
[34]:
# das da geht nicht: s = "abc"def"
[35]:
s = 'abc"def'
print(s)
abc"def
[36]:
s = "abc'def"
print(s)
abc'def
[37]:
s = "abc\"def'ghi"
print(s)
abc"def'ghi

Multiline Strings

[38]:
s = '''Hallo
Welt,
wie geht's denn so?
Hier ist eine vierte Zeile auch noch.
'''
print(s)
Hallo
Welt,
wie geht's denn so?
Hier ist eine vierte Zeile auch noch.

[39]:
s = 'Hallo\nWelt,\nwie geht\'s denn so?\n'
print(s)
Hallo
Welt,
wie geht's denn so?

Raw Strings

\n ist ein Linefeed. \P und \M haben keine spezielle Bedeutung und bleiben unangetastet.

[40]:
path = 'C:\Programme\Mein Glump\nebenlaeufigkeitsdemo'
print(path)
C:\Programme\Mein Glump
ebenlaeufigkeitsdemo

Raw Strings sind dafür die Lösung.

[41]:
path = r'C:\Programme\Mein Glump\nebenlaeufigkeitsdemo'
print(path)
C:\Programme\Mein Glump\nebenlaeufigkeitsdemo

f-Strings

f-Strings gibts ab Python 3.6: sehr feine Formatierungsmöglichkeit.

[42]:
var1 = 'hallo'
var2 = 'hello'
pfx_var1 = 'var1='
pfx_var2 = 'var2='
[43]:
ausgabe = pfx_var1+var1+', '+pfx_var2+var2
print(ausgabe)
var1=hallo, var2=hello
[44]:
var1 = 42
var2 = 666
pfx_var1 = 'var1='
pfx_var2 = 'var2='
[45]:
ausgabe = pfx_var1+str(var1)+', '+pfx_var2+str(var2)
print(ausgabe)
var1=42, var2=666

Bessere Formatierung: nicht potschert, mit f-String (seit Python 3.6)

[46]:
var1 = 'hallo'
var2 = 'hello'
ausgabe = f'var1={var1}, var2={var2}'
print(ausgabe)
var1=hallo, var2=hello
[47]:
var1 = 42
var2 = 666
print(f'var1={var1}, var2={var2}')
var1=42, var2=666

Konvertierungen

[48]:
s = '666'
type(s)
[48]:
str
[49]:
i = int(s)
print(i)
666
[50]:
try:  # notwendig, um jupyter notebook nicht abzubrechen. bitte ignorieren
    s = 'abc'
    i = int(s)
except ValueError as e:
    print(e)
invalid literal for int() with base 10: 'abc'
[51]:
s = '42.666'
f = float(s)
print(f)
42.666
[52]:
s = '10'
i = int(s, 16)
print(i)
16

Boolean

[53]:
1 < 2
[53]:
True
[54]:
2 < 1
[54]:
False
[55]:
b = True
print(type(b))
<class 'bool'>

Zum Beispiel:

[56]:
liste = [3,2,1,4]
gefunden = False
for wert in liste:
    if wert == 4:
        gefunden = True
print(gefunden)
True

Präzendenzregeln: and vor or

[57]:
True and False
[57]:
False
[58]:
True or False
[58]:
True
[59]:
True and True
[59]:
True
[60]:
False and True
[60]:
False
[61]:
True or False and True
[61]:
True
[62]:
False and True or False
[62]:
False

ist das gleiche wie:

[63]:
(False and True) or False
[63]:
False
[64]:
True and False or True
[64]:
True

ist das gleiche wie:

[65]:
(True and False) or True
[65]:
True

6.4 Literale für primitive Datentypen

6.5 Variablen und Konstanten

6.6 Operatoren

6.7 Ausdrücke und Operatorrangfolgen

6.8 Übungen

Zwei Variablen vertauschen

[66]:
var1 = 42
var2 = 666
[67]:
tmp = var1
var1 = var2
var2 = tmp
[68]:
print('var1:', var1, 'var2:', var2)
var1: 666 var2: 42

Zwei Variablen vertauschen mit Tuple Unpacking

[69]:
var1 = 42
var2 = 666
[70]:
var1, var2 = var2, var1
[71]:
print('var1:', var1, 'var2:', var2)
var1: 666 var2: 42

Übung 1, 5.

[72]:
value1 = 501
value2 = 799
value3 = 400
[73]:
(value1 > 500) or (value2 < 800) and not (value3 == 400)
[73]:
True
[74]:
(value1 > 500) or ((value2 < 800) and not (value3 == 400))
[74]:
True

7 Kontrollstrukturen

7.1 Anweisungen und Folgen

7.2 Bedingungen und Kontrollstrukturen

7.3 Grundlagen zu Verzweigungen

7.4 Bedingte Anweisung

7.5 Verzweigung

7.6 Geschachtelte Verzweigung

7.7 Mehrfache Verzweigung (Fallauswahl)

7.8 Schleifen

7.9 Zählergesteuerte Schleifen (Iteration)

7.10 Kopfgesteuerte bedingte Schleife

7.11 Fußgesteuerte bedingte Schleife

(jf) break und continue

(jf) Sequential Datatypes

[75]:
s = "Hello World"
[76]:
len(s)
[76]:
11

In Python beginnt man bei 0. In C, Java und C# (und vielen anderen Sprachen) auch.

[77]:
s[1]
[77]:
'e'
[78]:
s[0]
[78]:
'H'
[79]:
s[10]
[79]:
'd'

Negative Indizes erscheinen komisch, sind aber logisch.

[80]:
s[len(s)-1]
[80]:
'd'
[81]:
s[-1]
[81]:
'd'
[82]:
l = ['erstes element', 2, 'drei']
[83]:
l[0]
[83]:
'erstes element'
[84]:
l[-1]
[84]:
'drei'

(jf) for Loops

[85]:
s = 'Hello World'

Bequem:

[86]:
for character in s:
    print(character)
H
e
l
l
o

W
o
r
l
d
[87]:
i = 0
while i < len(s):
    print(s[i])
    i += 1
H
e
l
l
o

W
o
r
l
d

Umständlich:

[88]:
l = ['erstes element', 2, 'drei']
for element in l:
    print(element)
erstes element
2
drei

Iteration mit Dictionaries

[89]:
mapping_table = {
    'null': 'zero',
    'eins': 'one',
    'zwei': 'two',
    'drei': 'three',
    'vier': 'four',
    'fünf': 'five',
    'sechs': 'six',
    'sieben': 'seven',
    'acht': 'eight',
    'neun': 'nine',
}
[90]:
mapping_table['sieben']
[90]:
'seven'
[91]:
mapping_table['zehn'] = 'ten'
[92]:
mapping_table['zehn']
[92]:
'ten'

Iteration über ein Dictionary and sich iteriert über die Keys.

[93]:
for e in mapping_table:
    print(e)
null
eins
zwei
drei
vier
fünf
sechs
sieben
acht
neun
zehn

Explicit is better than implicit.

[94]:
for e in mapping_table.keys():
    print(e)
null
eins
zwei
drei
vier
fünf
sechs
sieben
acht
neun
zehn

Iteration über die Values.

[95]:
for e in mapping_table.values():
    print(e)
zero
one
two
three
four
five
six
seven
eight
nine
ten

Iteration über Key/Value Paare: Tuple

[96]:
for e in mapping_table.items():
    print(e)
('null', 'zero')
('eins', 'one')
('zwei', 'two')
('drei', 'three')
('vier', 'four')
('fünf', 'five')
('sechs', 'six')
('sieben', 'seven')
('acht', 'eight')
('neun', 'nine')
('zehn', 'ten')

Manuelles Tuple Unpacking

[97]:
for e in mapping_table.items():
    key = e[0]
    value = e[1]
    print(f'key={key}, value={value}')
key=null, value=zero
key=eins, value=one
key=zwei, value=two
key=drei, value=three
key=vier, value=four
key=fünf, value=five
key=sechs, value=six
key=sieben, value=seven
key=acht, value=eight
key=neun, value=nine
key=zehn, value=ten

Tuple Unpacking mit Hilfe der Sprache

[98]:
for key, value in mapping_table.items():
    print(f'key={key}, value={value}')
key=null, value=zero
key=eins, value=one
key=zwei, value=two
key=drei, value=three
key=vier, value=four
key=fünf, value=five
key=sechs, value=six
key=sieben, value=seven
key=acht, value=eight
key=neun, value=nine
key=zehn, value=ten

Ausflug: Vertauschen von zwei Variablen

[99]:
var1 = 666
var2 = 42

Potschert:

[100]:
tmp = var1
var1 = var2
var2 = tmp
print(f'var1={var1}, var2={var2}')
var1=42, var2=666

Mit Tuple Unpacking:

[101]:
var1, var2 = var2, var1
print(f'var1={var1}, var2={var2}')
var1=666, var2=42

(jf) The range() Function

Primzahlen checken, der Algorithmus: 1 ist keine Primzahl Sonst: von 2 bis eins unter der zu checkenden Zahl, probiere alle durch, ob die net gach ein Teiler der zu checkenden Zahl ist.

[102]:
zahl = 15
teiler_kandidaten = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

for n in teiler_kandidaten:
    if zahl % n == 0:
        print('nicht prim')
        break
else:
    print('prim')
nicht prim

Wenn ich jetzt zum Beispiel die Zahl 7, und nicht 15, checken will, muss ich gleichemaßen die teiler_kandidaten ändern.

[103]:
zahl = 7
teiler_kandidaten = [2, 3, 4, 5, 6]

for n in teiler_kandidaten:
    if zahl % n == 0:
        print('nicht prim')
        break
else:
    print('prim')
prim

Raymond Hettinger: “There must be a better way”

[104]:
zahl = 15

for n in range(2, zahl):
    if zahl % n == 0:
        print('nicht prim')
        break
else:
    print('prim')
nicht prim

Der Vollständigkeit halber (steht in der Doku auf python.org): was kann range() noch?

[105]:
for i in range(10):
    print(i)
0
1
2
3
4
5
6
7
8
9

Das gleiche, explizit mit 0 startend

[106]:
for i in range(0, 10):
    print(i)
0
1
2
3
4
5
6
7
8
9
[107]:
for i in range(2, 10):
    print(i)
2
3
4
5
6
7
8
9

Von inklusive 2 bis exklusive 15, Schrittweite 3

[108]:
for i in range(2, 15, 3):
    print(i)
2
5
8
11
14

7.12 Schnellübersicht

7.13 Sprunganweisungen

7.14 Endlosschleifen

7.15 Übungen

8 Elementare Datenstrukturen

8.1 Warum werden Datenstrukturen benötigt?

(jf) Compound Datatypes

(jf) References, (Im)mutability

Variable a referenziert ein Objekt vom Typ int mit Wert 42

[109]:
a = 42
[110]:
hex(id(a))
[110]:
'0x7f24f2ac0e50'

b zeigt auf das selbe Objekt (gleiche ID)

[111]:
b = a
[112]:
hex(id(b))
[112]:
'0x7f24f2ac0e50'

int is immutable, deswegen muss += ein neues Objekt ablegen

[113]:
b += 1
[114]:
hex(id(b))
[114]:
'0x7f24f2ac0e70'
[115]:
b
[115]:
43
[116]:
a
[116]:
42
[117]:
hex(id(b))
[117]:
'0x7f24f2ac0e70'
[118]:
hex(id(a))
[118]:
'0x7f24f2ac0e50'

(jf) Laufzeitverhalten bei der Suche (der in Operator)

Liste

Suche in einer Liste kann nur sequentielle Suche sein. Arschlangsam.

[119]:
l = [4, 7, 1, 2, 89, 1, 5]
[120]:
key = 5
num_comparisons = 0
for element in l:
    num_comparisons += 1
    if element == key:
        print('gefunden')
        break
else:
    print('nicht gefunden')
print(f'num_comparisons={num_comparisons}')
gefunden
num_comparisons=7

Der in Operator macht das bequemer. Aber Vorsicht mit Listen, wie gesagt.

[121]:
5 in l
[121]:
True

Set

set ist eine Datenstruktur, die für schnelles Suchen, Einfügen und Löschen optimiert ist. Perfekt für den in Operator.

[122]:
s = {4, 7, 1, 2, 89, 1, 5}
5 in s
[122]:
True

8.2 Arrays

8.3 Eindimensionale Arrays

8.4 Records

8.5 Zeichenketten

8.6 Tupel und Listen

[123]:
l = [1, 'zwei', 3]
[124]:
l.append('vier')
l
[124]:
[1, 'zwei', 3, 'vier']

Einitreten zwischen ‘zwei’ und 3:

[125]:
l.insert(2, 'einitreten')
l
[125]:
[1, 'zwei', 'einitreten', 3, 'vier']

Tuple mit einem Element (Jo Himmi!)

[126]:
t = ('ein element')
t
[126]:
'ein element'
[127]:
type(t)
[127]:
str
[128]:
t = ('ein element',)

Der Hintergrund (Operator Präzedenz)

[129]:
1 + 2 * 3
[129]:
7
[130]:
(1 + 2) * 3
[130]:
9
[131]:
(1+2)
[131]:
3

sorted, reversed

[132]:
l = [1, 2, 3]
[133]:
reversed(l)
[133]:
<list_reverseiterator at 0x7f24ec31d190>
[134]:
for elem in reversed(l):
    print(elem)
3
2
1
[135]:
l = [3, 2, 4, 6, 1]
[136]:
sorted(l)
[136]:
[1, 2, 3, 4, 6]
[137]:
for elem in sorted(l):
    print(elem)
1
2
3
4
6

List Comprehension

[138]:
def squares(l):
    result = []
    for elem in l:
        result.append(elem**2)
    return result

meine_liste = [1, 2, 3, 4, 5, 6, 7, 8]
for elem in squares(meine_liste):
    print(elem)
1
4
9
16
25
36
49
64
[139]:
meine_liste = [1, 2, 3, 4, 5, 6, 7, 8]
for elem in [elem**2 for elem in meine_liste]:
    print(elem)
1
4
9
16
25
36
49
64
[140]:
meine_liste = [1, 2, 3, 4, 5, 6, 7, 8]
for elem in [elem**2 for elem in meine_liste if elem % 2 == 0]:
    print(elem)
4
16
36
64

8.7 Dictionaries

[141]:
mapping_table = {
    'null': 'zero',
    'eins': 'one',
    'zwei': 'two',
    'drei': 'three',
    'vier': 'four',
    'fünf': 'five',
    'sechs': 'six',
    'sieben': 'seven',
    'acht': 'eight',
    'neun': 'nine',
}
[142]:
mapping_table['eins']
[142]:
'one'

[] bricht beinhart mit einer Exception (KeyError) ab, wenn das ELement nicht existiert.

[143]:
try:   # necessary to keep jupyter notebook going
    mapping_table['elf']
except Exception as e:
    print(type(e), e)
<class 'KeyError'> 'elf'

get() hingegen liefert None, wenn das ELement nicht existiert. Je nachdem, wie man es lieber hat.

[144]:
value = mapping_table.get('neun')
if value is None:
    print('nicht gefunden')
else:
    print(f'gefunden, wert {value}')
gefunden, wert nine
[145]:
value = mapping_table.get('zehn')
if value is None:
    print('nicht gefunden')
else:
    print(f'gefunden, wert {value}')
nicht gefunden

Der zweite Parameter wird returned, wenn das Element nicht existiert.

[146]:
value = mapping_table.get('zehn', 'jessas')
value
[146]:
'jessas'

(jf) Records in Python

8.8 Mengen

8.9 Besondere Datenstrukturen anhand von Stapel (Stack) und Schlangen (Queue)

8.10 Übungen

Übung 1

[147]:
numbers = (0, 10, 12, 4, 7, 20, 21, 13)

Versuch 1: in und index()

[148]:
zahl = 20
[149]:
if zahl in numbers:
    pos = numbers.index(zahl)
    print(f'{zahl} gefunden, position {pos}')
else:
    print('nicht gefunden')
20 gefunden, position 5

Versuch 2: Zurück zu while

[150]:
i = 0
while i < len(numbers):
    if numbers[i] == zahl:
        print(f'gefunden, position {i}')
        break
    i += 1
else:
    print('nicht gefunden')
gefunden, position 5

Versuch 3: hybrid potschert mit for

[151]:
pos = 0
for elem in numbers:
    if elem == zahl:
        print(f'gefunden, position {pos}')
        break
    pos += 1
else:
    print('nicht gefunden')
gefunden, position 5

Versuch 4: supergscheit mit for und enumerate()

[152]:
for pos, elem in enumerate(numbers):
    if elem == zahl:
        print(f'gefunden, position {pos}')
        break
else:
    print('nicht gefunden')
gefunden, position 5

9 Methoden, Prozeduren und Funktionen

[153]:
def maximum(a, b):
    if a < b:
        return b
    else:
        return a
[154]:
print(maximum(42, 666))
666

Funktionspointer (so heissts in C): Funktionsobjekte (Python)

Funktionen sind in Wirklichkeit nur Namen für Funktionsobjekte. Ordinäre Variablen, die man zuweisen etc. kann.

[155]:
print(type(maximum))
<class 'function'>
[156]:
blah = maximum
[157]:
print(type(blah))
<class 'function'>

Aufruf des Funktionsobjektes, auf das auch maximum zeigt. Nur halt über den alternativen Namen blah.

[158]:
print(blah(42, 666))
666

(jf) Primzahlenbeispiel mit Funktion

[159]:
def is_prime(num):
    # low hanging fruit
    if num == 1:
        return False

    # gemmas an
    for divisor_candidate in range(2, num//2+1):
        if num % divisor_candidate == 0:
            return False
    else:
        return True


n = 1
print(f'{n} prime: {is_prime(n)}')
n = 2
print(f'{n} prime: {is_prime(n)}')
n = 3
print(f'{n} prime: {is_prime(n)}')
n = 4
print(f'{n} prime: {is_prime(n)}')
n = 11
print(f'{n} prime: {is_prime(n)}')
1 prime: False
2 prime: True
3 prime: True
4 prime: False
11 prime: True

(jf) Local vs. Global Scope

Laufzeitfehler: Variable existiert nicht

[160]:
def f():
    print(x)
[161]:
try:  # wegen jupyter notebook
    f()
except Exception as e:
    print(e)
name 'x' is not defined

Lokale Variable

Dogma: wenn an einen Namen zugewiesen wird, der im aktuellen Context (“wir sind in Funktion f”) noch nicht existiert, wird in eben diesem Context dieser Name (diese Variable) erzeugt.

[162]:
def f(param):
    meine_variable = param * 7
    print(meine_variable)
f(5)
35

Globale Variable (lesen)

[163]:
def f():
    print(eine_variable)
[164]:
eine_variable = 666
f()
666

Globale Variable (schreiben/zuweisen)

Hier wird eine lokale Variable angelegt, die nichts mit der globalen zu tun hat. Streng nach obigem Dogma.

[165]:
eine_variable = 7
def f():
    eine_variable = 666
    print(f'local: {eine_variable}')

f()
print(f'global: {eine_variable}')
local: 666
global: 7

Wenn man eine globale Variable will, muss man es extra dazusagen. Nicht wie in Javascript.

[166]:
eine_variable = 7
def f():
    global eine_variable
    eine_variable = 666
    print(f'local: {eine_variable}')

f()
print(f'global: {eine_variable}')
local: 666
global: 666

Fehlerfall von Peter

[167]:
noch_eine_globale_variable = 42
[168]:
def increment_diese_variable():
    noch_eine_globale_variable = noch_eine_globale_variable + 1
[169]:
try:   # wegen jupyter notebook schon wieder
    increment_diese_variable()
except Exception as e:
    print(e)
local variable 'noch_eine_globale_variable' referenced before assignment

9.1 Unterprogramme

9.2 Parameterübergabe

9.3 Parameterübergabe als Wert

9.4 Parameterübergabe über Referenzen

9.5 Rückgabewerte von Funktionen oder Methoden

9.6 Innere Funktionen - Closures

9.7 Standardbibliotheken und Built-in-Funktionalitäten

9.8 Übungen