basics michael poeltl © 2011-2013 |
sind ein sehr sehr wichtiger Bestandteil des Programmierens überhaupt! Kontrollstrukturen helfen mir, den Programmfluss (Programmablauf) in geordnete Bahnen zu lenken:
Wenn die Fußgängerampel rot zeigt, dann bleibe stehen, ansonsten darfst du über die Straße gehen.
Man stellte also etwas zur Bedungung.
Genauso redet man in der jeweiligen Programmiersprache. Wenn das ist, dann tue jenes
. Oder, Nimm aus dem Plastiksackerl (einzeln) Dinge heraus. Überprüfe, ob sie in Ordnung sind und gebe diese in die Schultasche
. In diesem Fall haben wir die Aktion (Ding aus Sackerl raus und in die Schultasche rein), die solange durchgeführt wird, wie es Dinge im Plastiksackerl gibt. Es hört erst dann auf, wenn das Sackerl leer ist. Mit den Objekten, die nicht in Ordnung waren, kann man sich dann auch noch gesondert überlegen, was man damit tut.
Dies war ein Beispiel für eine Schleife, wie wir weiter unten noch hören werden.
Kontrollstrukturen sind sehr wichtig und zählen sicher zum A und O der Programmiererei. Das MUSS man einfach können!!!
Hier nun die erste Kontrollstruktur - if-elif-else.
Tue das nur, wenn das zutrifft oder jenes nicht zutrifft!
if <Bedingung>: pass elif <oder diese Bedingung>: pass else: pass
pass ist, wie true im shellscripting; tut nix, aber lässt es funktionieren
, weil die Syntax richtig ist (ich nutze pass gerne dann, wenn ich beim scripten weiß, dass ich diese oder jene Funktion brauchen werde. Und wenn die Funktion da steht, kann man nicht drauf vergessen!
Dann schreibe ich die Funktions-Kopfzeile hin, und gib ihr ein pass). pass lässt sich aber auch bei vielen anderen Angelegenheiten gut einsetzen, wie eben beim Skizzieren von if-conditions. Es tut nix und ist eigentlich immer wahr.
Oben siehst Du einen Block aus mehreren Zeilen, wo manche Zeilen gleich weit eingerü;ckt sind.
Die Einrückung (indentation) ist ein WESENTLICHER Bestandteil des Erscheinungsbilds eines python-scripts. Und ich erkläre es auch gleich.
Einrücken betont die logische Struktur des Programms. Da diese Einrückung in python erzwungen ist (sonst gibt es eine IndentationError Exception), ist die gute/bessere Lesbarkeit von python-programmen automatisch gegeben.
Die if-Abfrage besteht für gewöhnlich aus einem Kopfteil und einem Bauchteil. Und übersetze Dir if mit wenn oder falls. Wenn das zutrifft, dann tue dieses und jenes (dieses und jenes
ist dann der eingerückte Block unter der Zeile, die mit if beginnt.
if alter < 16: print ("Du darfst nicht rauchen") print ("muss um 22 Uhr zu Hause sein!") print ("Hat noch ncht die Matura gemacht.")
Der Block, der zur if-Abfrage gehört, wird durch code-Zeilen gebildet, die dieselbe Einrückungstiefe haben!
Es wird empfohlen (und ich tu's auch so), dass man mit 4 Leerzeichen einrückt (hab' im vim dementsprechend TAB präpariert).
Ausnahme mit der Bauchbildung
unter der Kopfzeile ist ein Einzeiler im Bauch der if-Abfrage - wenn es nur ein Ausdruck ist, dann kannst den auch neben den Doppelpunkt schreiben, wenn Du willst.
if <Bedungung>: <eine Anweisung>
ABER
Laut Idiomatic python
ist diese Schreibweise zu unterlassen, selbst wenn sie möglich wäre.
Nur wenn die Bedungung wahr ist, wird die Anweisung ausgeführt!
wahr => True
wann ist die Bedingung falsch?
5 < 2ist falsch (False))
if False:geschrieben wird
ist nicht gleich, oder ist keine Liste
if a != x: pass
if not <Bedungung>: pass #not als Verneiner
oder
if ! <Bedungung>: pass #das Rufzeichen (bang) als Verneiner
und so kommen wir zu unseren ersten Beispielen.
>>> d = '' >>> if d: ... print ('leer') ... >>> if not d: ... print ('leer') ... leer >>>
Fehlt uns noch ein Beispiel mit if-elif-else
>>> vorname = 'michael' >>> if vorname == 'erika': ... print (vorname) ... elif vorname == 'Thomas': ... print (vorname) ... else: ... print ('Vorname nicht erraten!') ... Vorname nicht erraten! >>>
Jetzt noch UND und ODER Verknüpfte Bedingungen
>>> if vorname == 'thomas' or vorname == 'michael': ... print ('einer der beiden Vornamen stimmt') ... einer der beiden Vornamen stimmt >>> if vorname == 'michael' or vorname == 'thomas': ... print ('einer der beiden Vornamen stimmt') ... einer der beiden Vornamen stimmt >>>
Wie Du ja schon weisst, braucht bei einer OR-Verknüpfung nur eine der beiden Aussagen (links und rechts vom OR) wahr sein.
Im Gegensatz zu and...
>>> alter = 44 >>> if vorname == 'michael' and alter < 50: ... print ("Vorname ist {}, und er ist unteer 50 Jahre alt".format(vorname)) ... Vorname ist michael, und er ist unteer 50 Jahre alt >>>
bedingter Operator(in C)
>>> anzahl = 5 >>> leute = 10 if anzahl >= 10 else 5 >>> print (leute) 5 >>>
Und somit könnte man das gleich in eine print-function packen.
>>> print('{0} anzahl sinds {1} leute'.format(anzahl, 10 if anzahl >= 10 else 5)) 5 anzahl sinds 5 leute >>>
Das kann manchmal sehr hilfreich sein!
Die Syntax sieht so aus:
for i in <iterierbares Objekt>: print (i) else: <noch-was, wenn for durchgelaufen ist und nicht durch break beendet wurde>
Das ist also eine Schleife. Der Name Schleife (loop) weist darauf hin, dass es bei der Abarbeitung der code-Zeilen eine Art Kreisbewegung
gibt.for i in
übersetzt man sich am besten mitfür jedes i in
in? worin? in einer liste, in einem tuple, in einem string
for i in 'michael'
würde bewirken, dass im 1. Umlauf i auf 'm' zeigt, im 2. Umlauf auf 'i', usw. (die Umläufe nennt man auch Iterationen).
Es gibt die Kreisbewegung, und i nimmt pro Umlauf einen anderen Wert (oder besser den nächsten Wert einer Sequenz oder eines Objekts, wo man eben duchwandern
kann an. Durchwandern durch ein Objekt kann man dann, wenn das Objekt iterable (duchwanderbar) ist.
i rückt also eine Stelle im iterierbaren Objekt weiter, und zeigt dann auf das Objekt an der Stelle, die gerade dran ist; so geht es weiter in den Bauch der for-Schleife.
Der Block, der zur Schleife gehört, ist wieder eingerückt unterhalb des Schleifenkopfes.
Wann ist die kreisbewegung beendet?
Genau dann, wenn das letzte Element des durchzuiterierenden Objekts erreicht wurde. Dann geht es unterhalb der Schleife weiter.
Mittels dem keyword break kann man eine Schleife vorzeitig verlassen
; dann geht es unterhalb der for-Schleife (gleiche Einrückungstiefe wie der Schleifenkopf) weiter.if a is 5: break
könnte im body der for-Schleife stehen.
>>> for i in 1: ... print (i) ... Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'int' object is not iterable >>> for i in 1,: ... print (i) ... 1 >>>
Ein kleiner Beistrich machte hier den Unterschied! 1 ist nicht durchwanderbar, weil 1 ein int-Objekt ist.
1, ist iterable, weil es eben eigentlich ein tuple ist; (1,)
.
>>> a = 1, >>> type(a)>>> type(a)() () >>>
Der else
-Zweig des for-Schleifen-Konstrukts sieht auf den ersten Blick fehl am Platze aus (solltest Du Dir (und ich mir) aber merken, dass es das gibt).
Alles im (eingerückten) Block unter else wird mit einer Ausnahme in jedem Fall ausgeführt. Es wird genau dann NICHT ausgeführt, wenn die for-Scleife vorzeitig durch die break-Anweisung verlassen wurde!
>>> for i in vorname: ... print (i) ... n i c o >>> for i in ( dict(), str(), tuple(), int(), list(), set() ): ... print( type(i) ) ... type(i)() ... <class 'dict'> {} <class 'str'> '' <class 'tuple'> () <class 'int'> 0 <class 'list'> [] <class 'set'> set() >>>
prompt:> cat ./for_else1.py #! /usr/bin/env python3 #for_else.py # fuer hofrat und fuer web vorname = 'nico' for i in vorname: print (i) else: print ('for-ging-durch\n') print ("for-ist-fertig\nhier geht's weiter") #end prompt:> ./for_else1.py n i c o for-ging-durch for-ist-fertig hier geht's weiter prompt>:
Also nochmals:
for-Schleife mit angehängten else - dann else - dann geht's unterhalb der for-Schleife weiter (beachte wieder die Einrückung beim for-Block und else-Block).
Als nächstes breche ich die for-Schleife vorzeitig ab.
prompt:> cat ./for_else2.py #! /usr/bin/env python3 #for_else2.py # fuer nico vorname = 'nico' for i in vorname: print (i) if i == 'c': break else: print ('for-ging-durch\n') print ("for-ist-fertig\nhier geht's weiter") #end prompt:> ./for_else2.py n i c for-ist-fertig hier geht's weiter prompt:>
Die Blockbildung durch Einrücken erspaert das Setzen von geschwungenen Klammern {}, wie man das in C machen müsste.
Da jeder python-Programmierer diser Regel (Blockbildung(en) durch Einrücken) unterworfen ist, wird python-code viel lesbarer! Indentation (Einrückung) ermöglicht erst die besere Lesbarkeit jeglichen python-codes (aus meiner Sicht).
Hält man sich nicht an die Einrückungsvorgabe bekommt man ohnehin eine IdentationError-Exception.
Die if-Abfrage im obigen Beispiel gehörte zum for-Block; break gehörte zum if-Block (2x Einrücken, da if-Kopf gehört zum for-Block). Und so wird für Dich das alles hoffentlich immer klarer.
break bricht aus der for-Schleife aus, und die Abarbeitung weiterer code-Zeilen geht unterhalb des for-Konstrukts weiter. continue setzt hingegen die weitere Abarbeitung des code-Blocks der for-Schleife für die aktuelle Iteration aus und leitet eine neue Iteration (einen neuen Umlauf) ein.
>>> for i in 1,2,3,4,: ... if i == 2 or i == 4: ... continue ... print (i) ... 1 3 >>>
Hier noch ei nettes Beispiel, wie man Pi mit Hilfe eines Kettenbruchs bis auf die Milliardstel-Stelle genau berechnen kann.
Hier eine Lösung in python3.
user@host:> cat ./basics/kettenpi.py #! /usr/bin/env python3 # -*- coding: utf-8 -*- ''' berechne die ersten sechs Stellen von Pi mittels 1 pi = 3 + --------------------------- 1 7 + ----------------------- 1 15 + ------------------ 1 1 + -------------- 1 292 + -------- 2 ''' __author__ = 'michael poeltl' __version__ = '0.01' __license__ = 'GPLv2.0' __email__ = 'michael.poeltl@univie.ac.at' def main(): ganz = (1, 15, 7, 3) nenner = 292 + 1/2 for i in ganz: nenner = (i*nenner+1)/nenner print( "pi berechnet nach 'kettenbruchentwicklung von pi' auf die Milliardstel-Stelle genau ergibt {}".format(nenner) ) if __name__ == '__main__': main() user@host:> python3 ./bsp/basics/kettenpi.py pi berechnet nach 'kettenbruchentwicklung von pi' auf die Milliardstel-Stelle genau ergibt 3.1415926534674368 user@host:>
Im Jahre 1202 veröffentlichte Leonardeo Fibonacci (aus Pisa) seine Fibonacci-Folge.
1 1 2 3 5 8 ... (immer aktuelle Zahl addiert mit seinem Vorgänger. Will man sich die ersten 20-Zahlen ausgeben lassen, kann man sich ebenfalls die for-Schleife zunutze machen.
>>> a,b = 1,1 >>> for i in range(20): ... print (a, end=' ') ... a,b = b,a+b ... else: ... print() ... 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 >>>
Das mit
a, b = b, a+b
ist so richtig python-like ;-).
Noch ein Beispiel zu einer for-Schleife in der for-Schleife:
>>> for i in range(4): ... for j in range(4): ... print( (i,j,), end=' ') ... (0, 0) (0, 1) (0, 2) (0, 3) (1, 0) (1, 1) (1, 2) (1, 3) (2, 0) (2, 1) (2, 2) (2, 3) (3, 0) (3, 1) (3, 2) (3, 3) >>>
Das ist eine ganz besondere Form, in der for-Schleifen eingesetzt werden.
vorher sollte ich aber noch range() hergezeigen!
>>> a = range(10) >>> print (type(a)) <class 'range'> >>> print (a) range(0, 10) >>>
aha! a zeigt auf ein Objekt vom Typ range (range-Objekt).
Gibt man sie aus print (a)
, dann wird dir die range angezeigt; der Bereich geht von 0 bis 10 --> und das bedeutet, dass es bei 0 beginnt, und bei 9 endet (von inclusive 0 bis exclusive 10).
Du kannst durch eine range durchiterieren; die einzelnen Zahlenobjekte werden hauptspeichersparend dann gebaut, wenn sie verlangt/gebraucht werden (on demand) und nicht zuerst alle Objekte der range in den Arbeitsspeicher *gelegt*, darauf wartend, abgeholt zu werden! Und ich werde an anderer Stelle noch genauer auf range eingehen, denn da gibt es noch ein paar Finessen, wie nur jedes zweite Objekt der range
oder rückwärts zählen.
>>> list_comp = [ x*x for x in range(100) if x % 2 ] >>> print (type(list_comp)) <class 'list'> >>> print (list_comp) [1, 9, 25, 49, 81, 121, 169, 225, 289, 361, 441, 529, 625, 729, 841, 961, 1089, 1225, 1369, 1521, 1681, 1849, 2025, 2209, 2401, 2601, 2809, 3025, 3249, 3481, 3721, 3969, 4225, 4489, 4761, 5041, 5329, 5625, 5929, 6241, 6561, 6889, 7225, 7569, 7921, 8281, 8649, 9025, 9409, 9801] >>>
Natürlich könntest Du dieses Ergebnis nehmen und für eine andere list-comprehension einsetzen - oder gar list-comprehension verschachteln.)
>>> list2_comp = [ i**0.5 for i in [ x*x for x in range(100) if x % 2 ] if not i % 3 ] >>> print (list2_comp) [3.0, 9.0, 15.0, 21.0, 27.0, 33.0, 39.0, 45.0, 51.0, 57.0, 63.0, 69.0, 75.0, 81.0, 87.0, 93.0, 99.0] >>>
oder eine Art vektorielles Produkt.
>>> list3_comp = [ a * b for a in range(1,4) for b in (0,5,10)] >>> print (list3_comp) [0, 5, 10, 0, 10, 20, 0, 15, 30] >>>
Zum Abrunden noch ein nettes *Zeichenspielchen*:
>>> for i in range( 32,128 ): ... print ( (i, chr(i),), end=' ') ... (32, ' ') (33, '!') (34, '"') (35, '#') (36, '$') (37, '%') (38, '&') (39, "'") (40, '(') (41, ')') (42, '*') (43, '+') (44, ',') (45, '-') (46, '.') (47, '/') (48, '0') (49, '1') (50, '2') (51, '3') (52, '4') (53, '5') (54, '6') (55, '7') (56, '8') (57, '9') (58, ':') (59, ';') (60, '<') (61, '=') (62, '>') (63, '?') (64, '@') (65, 'A') (66, 'B') (67, 'C') (68, 'D') (69, 'E') (70, 'F') (71, 'G') (72, 'H') (73, 'I') (74, 'J') (75, 'K') (76, 'L') (77, 'M') (78, 'N') (79, 'O') (80, 'P') (81, 'Q') (82, 'R') (83, 'S') (84, 'T') (85, 'U') (86, 'V') (87, 'W') (88, 'X') (89, 'Y') (90, 'Z') (91, '[') (92, '\\') (93, ']') (94, '^') (95, '_') (96, '`') (97, 'a') (98, 'b') (99, 'c') (100, 'd') (101, 'e') (102, 'f') (103, 'g') (104, 'h') (105, 'i') (106, 'j') (107, 'k') (108, 'l') (109, 'm') (110, 'n') (111, 'o') (112, 'p') (113, 'q') (114, 'r') (115, 's') (116, 't') (117, 'u') (118, 'v') (119, 'w') (120, 'x') (121, 'y') (122, 'z') (123, '{') (124, '|') (125, '}') (126, '~') (127, '\x7f') >>>
while <Bedingung>: do something do something else: do that if there was no break do that if there was no break
Die while-Schleife ist vor allem dann hilfreich, wenn Du nicht abschätzen kannst bzw. wenn Du nicht weißt, wieviele Umläufe s brauchen wird, um ein Ergebnis zu bekommen.
while True: do sometinng if this-is-true: break
Wenn die Bedingung im Schleifenkopf gleich auf True gesetzt wird, dann würde sich die Schleife ewig im Kreis drehen (Endlosschleife). Daher braucht es eine Abbruchbedingung. Zum Beispiel:Wenn zahl>10 dann brich ab.
if zahl > 10: break
break/continue gibt es für for/while Schleifen und sind sehr hilfreich.
continue verhindert unnötige *Umrundungen* indem es bei Zutreffen einer condition einen neuen Umlauf vorzeitig einleitet. Mit break bricht man aus der aktuellen Schleife aus.
range(x,y):
x,y sind natürliche Zahlen; Die Spannweite geht von x bis zu y-1. range(2,18) zählt also von 2 bis 17.
Wollte man nur jeden zweiten Wert, dann würde manrange(2,18,2)
schreiben.
>>> bereich1 = range(2,18) >>> print (list(bereich1)) [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] >>> bereich1 = range(2,18,2) >>> print (list(bereich1)) [2, 4, 6, 8, 10, 12, 14, 16] >>>
Interessant ist auch, wie man es anstellt, dass von hinten nach vorn
gezählt wird.
range(y,x,-1)
, und schon wird rückwärts gezählt, von y bis x+1, also range(10,1) geht von 10 bis 2.
>>> bereich2 = range(10,1,-1) >>> print (list(bereich2)) [10, 9, 8, 7, 6, 5, 4, 3, 2] >>> bereich2 = range(10,1,-2) >>> print (list(bereich2)) [10, 8, 6, 4, 2] >>> bereich2 = range(10,1,1) >>> print (list(bereich2)) [] >>>
range(10,1,1)
will von 10 nach 1 *raufzählen*, daher auch die leere Menge.
Ein Anwendungsbeispiel fürs Runterzählen könnte die Berechnung der Fakultät (n!) einer Zahl sein (siehe fakt.py in der Beispielsammlung)
user@host:~> cat fact.py #! /usr/bin/env python3 ''' einfache Berechnung der Faktorielle bsp zur Veranschaulichung von range() ''' __author__ = 'michael poeltl' __version__ = '0.01' __license__ = 'GPLv3' def main(): while True: try: zahl = eval(input("Fakultaet von welcher Zahl? ")) if isinstance(zahl,int) and zahl > -1: break except: continue f = 1 for i in range(zahl,1,-1): f *= i print ("Die Fakultaet von {0} ({0}!) ist {1}".format(zahl,f)) if __name__ == '__main__': main() user@host:~> ,/fakt.py Fakultaet von welcher Zahl? 10 Die Fakultaet von 10 (10!) ist 3628800 user@host:~>