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#